首页 前端知识 vue手搓自定义日历, 同步节假日

vue手搓自定义日历, 同步节假日

2024-02-22 11:02:19 前端知识 前端哥 724 214 我要收藏

日历使用背景

        内网环境,无法使用外网及时同步节假日信息,所以要在内网创建日历,每年年初或头年年末同步一次节假日信息, 后续变更自己手动维护。

        发现element-ui的日历组件并不能满足我们的要求, 组件的属性太少,于是我仿着手搓了一个日历出来, 本着授之于鱼不如授之以渔的原则, 我将把思路和整体流程跟大家讲一下, 其他因为不同需求的关系, 需要大家自己丰衣足食。

  • 话不多说,先看效果图

日历效果图

  • 日历创建规则

        首先我们要确定日历头部, 是一二三四五六日, 还是日一二三四五六,这影响到我们生成日期的规则,我们暂时将前者定为规则一,后者定为规则二。

        第一步,我们要确定当月1号是周几, 拿今年9月为例, 2023年9月1号是周五, 按照规则一则需要展示上月4天的日期, 而按照规则二则需要向前5天。而后面的日期就很好办了, 按照正常日历,应该展示6*7方格, 42天的数据, 我们只需要找到当前展示的第一天,往后+41天就可以了。

代码

  • 前台vue

        前台代码没什么逻辑, 我把日期操作都放到了后台, 因为要同步节假日和其他操作, 所以放到后台是最好的选择, 前台代码直接复制粘贴就能用。

<template>
  <div class="app-main">
     <div class="demo-block">
      <div class="source">
        <div class="el-calendar">
          <div class="el-calendar__header">
            <div class="el-calendar__title">{{queryParams.year}}年{{queryParams.month}}月</div>
            <div class="el-calendar__button-group">
              <div class="el-button-group">
                <el-button type="button" size="mini" @click="lastMonth">上个月</el-button>
                <el-button type="button" size="mini" @click="toDay">今天</el-button>
                <el-button type="button" size="mini" @click="nextMonth">下个月</el-button>
              </div>
            </div>
          </div>
          <div class="el-calendar__body">
            <table class="el-calendar-table" cellspacing="0" cellpadding="0" >
              <thead>
                <th style="color: red;font-size: 20px;font-weight: 600;">日</th>
                <th style="font-size: 20px;font-weight: 600;">一</th>
                <th style="font-size: 20px;font-weight: 600;">二</th>
                <th style="font-size: 20px;font-weight: 600;">三</th>
                <th style="font-size: 20px;font-weight: 600;">四</th>
                <th style="font-size: 20px;font-weight: 600;">五</th>
                <th style="color: red;font-size: 20px;font-weight: 600;">六</th>
              </thead>
                <tbody>
                <tr
                  v-for="item in (holidayList)"
                  :key="item.id"
                >
                    <td
                    class="current"
                    v-for="(col, colIdx) in item"
                    :key="colIdx"
                    >
                      <div @click="selectDay(col)" :class="col.curDate==selectId?'el-calendar-day-active':'el-calendar-day'" :style="{color: col.isGray==0?(col.type==1||col.type==2?'red':'black'):(col.type==1||col.type==2?'#F3BCBC':'gray')}">
                        <span>{{col.day}}</span><br>
                        <span>{{col.showName}}</span>
                      </div>
                    </td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

export default {
  name: "holiday",
  data() {
    return {
      queryParams: {
        id: null,
        type: null,
        year: new Date().getFullYear(),
        month: new Date().getMonth()+1,
      },
      generateYear: new Date().getFullYear(),
      showSearch: true,
      holidayList: [],
      selectId: this.initSearchDate()
    };
  },
  mounted() {
  },
  created() {
    this.initDate();
  },
  methods: {
    initSearchDate() {
      var nowDate = new Date();
      var date = {
        year: nowDate.getFullYear(),
        month: nowDate.getMonth() + 1,
        day: nowDate.getDate()
      }
      return date.year + '-' + (date.month >= 10 ? date.month : '0' + date.month) + '-' + (date.day >= 10 ? date.day : '0' + date.day);
    },
    initDate(){
      this.holidayList= [];
      listHolid(this.queryParams).then(response => {
        let list = response.data;
        for (let row = 0; row < 6; row++) {
          this.holidayList.push(list.splice(0, 7));
        }
      });
    },

    selectDay(col){
      this.selectId = col.curDate;
      this.queryParams.id = col.id;
    },

    lastMonth(){
      if(this.queryParams.month==1){
        this.queryParams.year --;
        this.queryParams.month = 12;
      }else{
        this.queryParams.month --;
      }
      this.initDate();
    },
    toDay(){
      this.queryParams.year= new Date().getFullYear();
      this.queryParams.month = new Date().getMonth()+1;
      this.selectId = this.initSearchDate();
      this.initDate();
    },
    nextMonth(){
      if(this.queryParams.month==12){
        this.queryParams.year ++;
        this.queryParams.month = 1;
      }else{
        this.queryParams.month ++;
      }
      this.initDate();
    }

  }
};
</script>
<style lang="scss" scoped>
.app-main{
  background-color: #f6f6f6;
}

.demo-block {
  background-color: #fff;
  margin-top: 80px;
  margin-left: 25%;
  border: 1px solid #ebebeb;
  border-radius: 25px;
  width: 870px;
  height: 710px;
  .source{
    padding: 24px;
  }
}

.el-calendar {
  background-color: #fff;
}

.el-calendar__header {
  display: flex;
  justify-content: space-between;
  padding: 12px 20px;
  border-bottom: 1px solid #ebeef5;
}

.el-calendar__title {
  color: #000;
  align-self: center;
}

.el-button-group {
  display: inline-block;
  vertical-align: middle;
}

.el-calendar__body {
  padding: 12px 20px 35px;
}

table {
  display: table;
  border-collapse: separate;
  box-sizing: border-box;
  text-indent: initial;
  border-spacing: 1px;
  border-color: gray;
}

.el-calendar-table {
  table-layout: fixed;
  width: 100%;
  tr {
    display: table-row;
    vertical-align: inherit;
    border-color: inherit;
  }
}

.current{
  border: 1px solid #ebeef5;
  vertical-align: top;
  transition: background-color .2s ease;
}

.el-calendar-day{
  box-sizing: border-box;
  padding: 6px;
  height: 85px;
  line-height: 35px;
  text-align: center;
  :hover{
    cursor: pointer;
    background-color: #f2f8fe;
  }
}

.el-calendar-day-active{
  box-sizing: border-box;
  padding: 6px;
  height: 85px;
  line-height: 35px;
  text-align: center;
  background-color: #c9dff4;

}

.el-calendar-day-gray{
  box-sizing: border-box;
  padding: 6px;
  height: 85px;
  line-height: 35px;
  text-align: center;
  :hover{
    cursor: pointer;
    background-color: #f2f8fe;
  }
}

tbody {
  display: table-row-group;
  vertical-align: middle;
  border-color: inherit;
}
</style>

  • 后台代码

先看一下表结构, 表名称holiday_info, 表结构大家可根据自己需求自行构建

  • 实体类

isGray用来区分当前日期是否属于本月, type用来判断当前日期是否为假期和周末(即前台标红)

/**
 * 
 * @author bobo
 * @date 2023-09-23
 */
@Data
public class HolidayInfo extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    private Long id;

    /** 年 */
    private Integer year;

    /** 月 */
    private Integer month;

    /** 日 */
    private Integer day;

    /** 节日名称 */
    private String name;

    /** 展示内容 */
    private String showName;

    /** 星期 */
    private String weekName;

    /** 0工作日 1周末 2节日 3调休 */
    private Integer type;

    private String typeStr;

    /** 用于同步API节假日的key */
    private String keyDate;

    /** 日期 */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date curDate;

    /** 开始日期 */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date startDate;

    /** 结束日期 */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date endDate;

    /** 农历日 */
    private String lunars;

    /** 农历日期 */
    private String lunarsDate;

    /** 是否置灰 0不置灰 1置灰 */
    private Integer isGray;
}
  • 生成年度日历

生成农历日期的话, 网上搜索资源就可以了, 因为不是很重要而且网上资源很多我就不占地方了,如果找不到或者懒得找,参考如下链接:

https://blog.csdn.net/weixin_43860634/article/details/128847454

原创作者:   飞翔的佩奇

    /**
     * 生成年度日期
     *
     * @param holidayInfo 节假日实体类
     * @return 结果
     */
    @Override
    public AjaxResult generateYearData(HolidayInfo param){
        Integer year = param.getYear()==null?Integer.valueOf(DateUtils.dateTimeNow("yyyy")):param.getYear();
        holidayInfoMapper.deleteHolidayInfoByYear(year);
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.set(GregorianCalendar.DAY_OF_YEAR, 1);
        calendar.set(GregorianCalendar.YEAR, year);
        // 循环输出一年的日期
        List<HolidayInfo> list = new ArrayList<>();
        while (calendar.get(GregorianCalendar.YEAR) == year) {
            Date date = calendar.getTime();
            HolidayInfo holidayInfo = new HolidayInfo();
            holidayInfo.setYear(calendar.get(GregorianCalendar.YEAR));
            holidayInfo.setMonth(calendar.get(GregorianCalendar.MONTH)+1);
            holidayInfo.setDay(calendar.get(GregorianCalendar.DATE));
            //周名称
            holidayInfo.setWeekName(new DateFormatSymbols().getWeekdays()[calendar.get(GregorianCalendar.DAY_OF_WEEK)]);
            int weekDay = calendar.get(GregorianCalendar.DAY_OF_WEEK);
            int boo = (weekDay==1||weekDay==7)?1:0;
            //判断日期类型 0工作日 1周末 2节假日 3调休
            holidayInfo.setType(boo);
            holidayInfo.setKeyDate(DateFormatUtils.format(date, "MM-dd"));
            holidayInfo.setCurDate(date);
            CalendarUtils as = CalendarUtils.as(date);
            //农历日 初一~三十
            holidayInfo.setLunars(as.getChinaDayString());
            //农历日 例如: 二零三年九月初一
            holidayInfo.setLunarsDate(as.getChinaString());
            list.add(holidayInfo);
            calendar.add(GregorianCalendar.DAY_OF_YEAR, 1);
        }
        holidayInfoMapper.insertBatch(list);
        return AjaxResult.success(year+"年度日历已生成");
    }
  • 日历查询
    /**
     * 日历查询
     * 
     * @param holidayInfo 
     * @return AjaxResult 
     */
    @Override
    public AjaxResult selectHolidayInfoList(HolidayInfo holidayInfo )
    {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        //处理日期
        Integer year = holidayInfo .getYear();
        Integer month = holidayInfo .getMonth();
        GregorianCalendar calendar = new GregorianCalendar();
        // 重置时间为当月月初  例: 2023-09-01
        calendar.set(GregorianCalendar.YEAR, year);
        calendar.set(GregorianCalendar.MONTH, month-1);
        calendar.set(GregorianCalendar.DAY_OF_MONTH, 1);
        try {
            // 获取当月月初的week 需要注意的是  1代表着周末
            int week = calendar.get(GregorianCalendar.DAY_OF_WEEK);
            week = week==1?7:week-1;
            // 本月日历展示的起始日期 例: 2023-08-27
            calendar.add(GregorianCalendar.DAY_OF_YEAR, -week);
            holidayInfo .setStartDate(df.parse(df.format(calendar.getTime())));
            // 本月日历展示的结束日期 例: 2023-10-07
            calendar.add(GregorianCalendar.DAY_OF_YEAR, 41);
            holidayInfo .setEndDate(df.parse(df.format(calendar.getTime())));
            List<HolidayInfo> list = holidayInfoMapper.selectHolidayInfoList(holidayInfo );
            list.stream().forEach(item->{
                item.setIsGray(item.getMonth()==month?0:1);
            });
            return AjaxResult.success(list);
        } catch (ParseException e) {
            e.printStackTrace();
            return AjaxResult.error("时间转换异常");
        }
    }
    <sql id="selectHolidayInfoVo">
        select
            id,
            year,
            month,
            day,
            name,
            if((name is null or name = ''), lunars, name) showName,
            week_name,
            type,
            case type
               when 0 then '工作日'
               when 1 then '周末'
               when 2 then '节假日'
               when 3 then '调休'
            else '' end typeStr,
            key_date,
            cur_date,
            lunars
        from holiday_info
    </sql>

    <select id="selectHolidayInfoList" parameterType="HolidayInfo" resultMap="HolidayInfoResult">
        <include refid="selectHolidayInfoVo"/>
        <where>  
            <if test="startDate != null "> and cur_date &gt;= #{startDate}</if>
            <if test="endDate != null "> and cur_date &lt;= #{endDate}</if>
        </where>
    </select>
  • 同步节假日

我用api是提莫的神秘小站, 链接:免费节假日 API - 提莫的神秘小站

代码贴上:

     /**
     * 请求获取节假日信息
     */
    public static String getHoliday(Integer year) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try{
            URL realUrl = new URL("https://timor.tech/api/holiday/year/" + year);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
        }catch (Exception e) {
            System.out.println("调用HttpUtils.sendGet ConnectException: " + e);
        }
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            }
            catch (Exception ex) {
                System.out.println("调用in.close Exception: "+ ex);
            }
        }
        return result.toString();
    }

对于返回来的JSON数据,大家随意处理就行, 能看到我们只是对name和type进行了处理, 当然我选择了不用动脑,最笨的双重for循环

    /**
     * 同步节假日
     *
     * @param holidayInfo
     * @return 结果
     */
    @Override
    public AjaxResult syncHoliday(HolidayInfo holidayInfo){
        Integer year = holidayInfo.getYear()==null?Integer.valueOf(DateUtils.dateTimeNow("yyyy")):holidayInfo.getYear();
        String result = HldjcUtils.getHoliday(year);
        JSONObject parse = (JSONObject)JSONObject.parse(result);
        JSONObject holidayJson = (JSONObject)parse.get("holiday");
        Map<String, Object> map = holidayJson.toJavaObject(Map.class);
        List<HolidayInfo > holidayList = holidayInfoMapper.selectHolidayInfoListByYear(year);
        for (HolidayInfo holiday: holidayList) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                if(holiday.getKeyDate().equals(key)){
                    JSONObject valueJson = (JSONObject)JSONObject.parse(String.valueOf(entry.getValue()));
                    Boolean holidayFlag = (Boolean)valueJson.get("holiday");
                    String name = (String)valueJson.get("name");
                    holiday.setType(holidayFlag?2:3);
                    holiday.setName(name);
                    holidayInfoMapper.syncHolidayInfo(holiday);
                }
            }
        }
        return AjaxResult.success();
    }

到这一步就基本完事了,后续可以在日历上进行配置,或者生成操作。生成年度的时候,建议对已同步节假日的年度进行校验,否则就会覆盖了。而且对配置过的年度是否可以再次生成就要看实际的应用场景了。好了相信我讲的应该也够清楚了,大家一起努力吧

转载请注明出处或者链接地址:https://www.qianduange.cn//article/2482.html
标签
后端
评论
发布的文章

jQuery中的ajax

2024-03-07 08:03:42

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!