#夏日挑战赛# HarmonyOs- ArkUI(JS)自定义组件之日历控件 原创 精华

中软国际AIoT开发者社区
发布于 2022-7-5 16:55
浏览
2收藏

作者:曹琪娟

本文正在参加星光计划3.0–夏日挑战赛

前言

目前工作中同一个项目经常需要同时开发FA和H5两种形态。H5使用的主要是vue框架,FA则使用 HarmonyOS ArkUI(JS) 。HarmonyOS ArkUI(JS) 开发框架设计思想和语法与vue很类似,经常看到很多vue相关的日历控件,想着基于现掌握的 FA 相关知识,也尝试着编写一个简单的日历选择控件。由于最近忙项目,时间匆忙,就只是实现了一些简单的功能。

效果展示

#夏日挑战赛# HarmonyOs- ArkUI(JS)自定义组件之日历控件-鸿蒙开发者社区

实现代码

1. htm 代码

主要分为2个部分,日期输入框和日期选择面板。点击日期输入框会弹出日期选择面板。日期选择面板的日期部分固定为7 * 6 = 42 个日期进行填充。

<div class="pickDate">
  <div class="pick-input" @click="showPickDate">
    <image class="pick-img" src="./images/date-pick.png"></image>
<!--    <text class="date-input">{{`${formatDate.year}-${formatDate.month}-${formatDate.day}` }}</text>-->
    <text class="date-input">{{inputData }}</text>
  </div>
  <div class="pickPanel" show="{{isShowPickDate}}">
    <div class="pick-head">
      <text class="left-arrow" @click="changeMonth(-1)">{{'<'}}</text>
      <text class="date-head">{{ monthText }}</text>
      <text class="right-arrow" @click="changeMonth(1)">{{'>'}}</text>
    </div>
    <div class="week-days">
      <text class="week-day" for="weekdays" tid="$idx">{{$item}}</text>
    </div>
    <div class="pick-days">
      <div class="pick-day" for="getDays" tid="$idx" @click="dayClick($item)">
        <text class="day-text {{ (($item.status === 'prev') || ($item.status === 'next' ))? 'prev' : ''}} {{isToday($item.date) ? 'today' : ''}}
        " >{{getDate($item)}} </text>
      </div>
    </div>
  </div>
</div>

2. js 代码

js 主要计算出当前月的总天数和上个月要填充的天数和下个月要填充的天数,把这些日期值存放在一个日期列表里,用于UI的循环渲染显示。

export default {
  data: {
    isShowPickDate: false,
    monthText: '',
    tempValue: null,
    inputValue: null, // 输入框的值
    weekdays: ['一', '二', '三', '四', '五', '六','日',]
  },
  onInit() {
  },
  computed: {
    getDays () {
      const [year, month] = this.getYearMonthDay(this.tempValue);
      // 0 ~ 6, 需要将0转换为7
      let startWeek = new Date(year, month, 1).getDay();
      if (startWeek === 0) {
        startWeek = 7;
      }
      const prevLastDay = this.getPrevMonthLastDay(year, month);
      const curLastDay = this.getCurrentMonthLastDay(year, month);
      this.monthText = `${year}年${month + 1}月`
      const days = [...this.getPrevMonthDays(prevLastDay, startWeek), ...this.getCurrentMonthDays(curLastDay), ...this.getNextMonthDays(curLastDay, startWeek)];
      return days;
    },
    formatDate () {
      const [year, month, day] = this.getYearMonthDay(this.inputValue);
      return { year, month: month + 1, day };
    },
    inputData() {
      return `${this.formatDate.year}-${this.formatDate.month}-${this.formatDate.day}`
    }

  },
  showPickDate() {
    this.isShowPickDate = true;
    console.log('显示日期选择面板');
    this.tempValue = this.inputValue;
  },
  // 点击某一日期
  dayClick(e) {
    console.log('当前选择的日期');
    console.log(JSON.stringify(e))
    if (e.status !== 'current') return;
    this.tempValue = e.date;
    this.inputValue = this.tempValue;
    this.isShowPickDate = false;

  },
  getDate(item) {
    return item.date.getDate();
  },
  // 切换上个月 下个月
  changeMonth(value) {
    console.log('value' + value)
    const [year, month] = this.getYearMonthDay(this.tempValue);
    const monthNum = month + value;
    const val = this.tempValue.setMonth(monthNum)
    this.tempValue = new Date(val)
    console.log(this.tempValue)
  },
  // 获取前一个月天数
  getPrevMonthDays (prevLastDay, startWeek) {
    const [year, month] = this.getYearMonthDay(this.tempValue);
    const prevMonthDays = [];
    console.log('prevLastDay' + prevLastDay)
    for (let i = prevLastDay - startWeek+2; i <= prevLastDay; i++) {
      prevMonthDays.push({
        date: new Date(year, month - 1, i),
        status: 'prev'
      });
    }
    console.log('prevMonthDays' + prevMonthDays.length);
    return prevMonthDays;
  },
  // 获取当前月天数
  getCurrentMonthDays (curLastDay) {
    const [year, month] = this.getYearMonthDay(this.tempValue);
    const curMonthDays = [];
    for (let i = 1; i <= curLastDay; i++) {
      curMonthDays.push({
        date: new Date(year, month, i),
        status: 'current'
      });
    }
    console.log('curMonthDays' + curMonthDays.length);
    return curMonthDays;
  },
  // 获取下一个月天数
  getNextMonthDays (curLastDay, startWeek) {
    const [year, month] = this.getYearMonthDay(this.tempValue);
    const nextMonthDays = [];
    for (let i = 1; i <= 42 - startWeek - curLastDay + 1; i++) {
      nextMonthDays.push({
        date: new Date(year, month + 1, i),
        status: 'next'
      });
    }
    return nextMonthDays;
  },
  // 获取当前月的最后一天
  getCurrentMonthLastDay(year, month){
    return new Date(year, month + 1, 0).getDate();
  },
  // 获取上一个月的最后一天
  getPrevMonthLastDay(year, month){
    return new Date(year, month, 0).getDate();
  },
  // 获取年 月 日
  getYearMonthDay(value) {
    const date = value ? new Date(value) : new Date();
    const day = date.getDate();
    const month = date.getMonth();
    const year = date.getFullYear();
    return [year, month, day];
  },
  // 是否是今天
  isToday (date) {
    const [y1, m1, d1] = this.getYearMonthDay(date);
    const [y2, m2, d2] = this.getYearMonthDay(new Date);
    return y1 === y2 && m1 === m2 && d1 === d2;
  }
}

3. css 样式

.pickDate{
  padding-top: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.pick-input{
  position: relative;
  border: 1px solid black;
  width: 300px;
}
.pick-img{
  width: 40px;
  height: 40px;
}
.date-input{
  background-color: white;
  padding:0 10px;
  width: 180px;
  font-size: 24px;
}
/* 日期面板*/
.pickPanel{
  flex-direction: column;
  margin: 6px;
  width: 90%;
  height: 400px;
  background-color: #fff;
  border-radius: 10px;
  padding-top: 10px;
  box-shadow: 2px 2px 11 gray;
}
.pick-head{
  justify-content: space-between;
}
.date-head{
  font-size: 22px;
}
.left-arrow, .right-arrow{
  width: 40px;
  font-size: 24px;
  text-align: center;
}
.week-days{
  justify-content: space-between;
  margin-top: 10px;
}
.week-day{
  width: 16.285%;
  font-size: 18px;
  text-align: center;
}
.pick-days{
  flex-wrap: wrap;
  justify-content: space-between;
}
.pick-day{
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 54px;
  width: 14.285%;
}
.day-text {
  color: #323232;
  font-size: 22px;
}
.prev{
  color: rgba(0,0,0,.3);
}
.today {
  color: aqua;
}

总结

到这里简单的日期选择控件基本完成,这里的主要技术点就是对日期相关api的掌握,日期相关的计算和要清楚实现思路,其中还有很多不足,还有很多可优化的地方,后续有时间还会慢慢去研究,完善。也欢迎各位开发者一起讨论与研究,希望本次分享对大家有所帮助,如有问题欢迎指正。

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
6
收藏 2
回复
举报
1条回复
按时间正序
/
按时间倒序
只有敬亭山
只有敬亭山

刚好需要一个日历组件来做项目,收藏收藏

回复
2022-7-6 09:44:55
回复
    相关推荐