记独立开发的第一个项目——Xi-note记事

422 阅读5分钟

前言:

自学习以来一直想开发一个自己的项目,但迟迟没有灵感。前些日子在某站上看到一个up主开发的记事项目,发觉记事本原来也是大有可为之处的。于是自己也做了这样一个麻雀虽小五脏俱全的Web应用。希望能获得大佬们的建议,给和我同样的新人们一些启发。

项目概述

技术栈

前端:Vue2全家桶+less+axios+ECharts+cropper.js
后端:node.js+express+mysql+jsonwebtoken


在线预览地址:www.w-catnip.vip:2333/
源码地址:github.com/W-catnip/Xi…

功能概述

  • 支持跳转不同日期
  • 支持添加指定日期的待办事项
  • 支持添加每日的固定打卡事项
  • 支持事项的编辑、删除、完成状态更改
  • 支持当前日期的事项完成情况统计
  • 支持七天内的待办事项完成情况统计
  • 支持每日打卡全部完成情况统计
  • 支持用户的注册与登录
  • 支持用户资料更改及头像裁剪后更改

项目预览

1. 登录页

d93jl-c43gs

2. 主页

bzj6d-9v1zh

3. 数据页

lhg13-2f5ou

4. 设置页

h1ws0-l1a66 注:由于录屏限制,文件选择未在该图内展示


项目细节

尽管功能简单,但在开发过程中还是有遇到各种各样的问题,在下文中会记录一些印象较深的需求解决方案,如有错漏与不足,请各位不吝赐教。

日历的实现与折叠

实现目标:一个折叠时自动显示本周日期的可选择日期表。

实现

因为每个月的第一天是周几与每月的总天数不确定,所以我们只需要知道本月的第一天是周几,本月的天数,以及上一个月最后一天是多少号,将需要的42天做成一个数组,就可以实现一个7*6的日历表。

<script>
...
data(){
// 获得当前年月日
    return: {
        curDate: new Date().getDate(),
        curMonth: new Date().getMonth() + 1,
        curYear: new Date().getFullYear(),
        highLight: null,
    }
computed:{
    // 日历偏移参数,获得本月第一天是周几,用于空出需要上月日期补充的部分。
    delay() {
      let day = new Date(this.curYear, this.curMonth - 1, 1).getDay();
      let delay = day - 1;
      if (day === 0) {
        delay = 6;
      }
      return delay;
    },
    // 返回需要渲染的本月日期表
    monthList() {
      // new Date()最后一个参数是0,表示当月最后一天
      let date = new Date(this.curYear, this.curMonth, 0);
      // 通过当月最后一天的日期号,得到当月有多少天
      let days = date.getDate();
      let list = [];
      // 获取上月最后一天的日期
      let lastDays = new Date(this.curYear, this.curMonth - 1, 0).getDate();
      for (let i = 1; i < 43; i++) {
        // 用上个月的日期填充日历空缺部分
        if (i < this.delay + 1) {
          list.push({
            id: i, // 日期标识,用于高亮
            value: lastDays - this.delay + i,// 日期号
            notThisMonth: true, // 标记不是本月的日期,添加不同样式,判断是否绑定点击事件
          });
          // 本月日期
        } else if (i < days + this.delay + 1) {
          list.push({
            id: i,
            value: i - this.delay,
            notThisMonth: false,
          });
          // 下月日期填充空缺部分
        } else {
          list.push({
            id: i,
            value: i - days - this.delay,
            notThisMonth: true,
          });
        }
      }
      return list;
    },
}
...
</script>

// 渲染
<template>
...
     <ul>
          <li v-for="day in monthList" :key="day.id" class="monthList-day" :class="{
            notThisMonth: day.notThisMonth,
            selectDay: day.id === highLight,
          }"
          @click="!day.notThisMonth && handleSelectDay(day)">
            {{ day.value }}
          </li>
        </ul>
...
</template>

折叠

如何在折叠后保证只显示本周日期?

我采取的是一种比较取巧的方法,因为在该应用中,始终有一个日期处于被选择状态,而且每一行只显示7个日期,所以只需要获得该日期的id,就可以得知它在第几行。在点击折叠后计算需要向上移动多少个单位让其刚好在显示区域,而将其他日期表进行隐藏,就可以定位显示到所选择的日期所在的星期。

<script>
...
    computed:{
        // 日历盒子的位移参数即被选择的日期所在的行数。
        boxDisplacement() {
          return Math.ceil(this.highLight / 7);
        },
    },
    methods:{
         // 选择日期
         handleSelectDay(day) {
          this.highLight = day.id;
          this.curDate = day.value;
        },
        // 展开收起
       changeListOpen() {
          this.listIsOpen = !this.listIsOpen;
          if (!this.listIsOpen) {
            // 移动日期盒子只让本周日期显示
            this.$refs.monthList.style.top = `-${this.boxDisplacement * 3 + 1.7}rem`;
           } else {
            this.$refs.monthList.style.top = "-2rem";
          }
        },
    },
    mounted(){
        //初始化
        this.highLight = this.curDate + this.delay;
        this.$refs.monthList.style.top = `-${this.boxDisplacement * 3 + 1.7}rem`;
    }
 ...
</script>

<template>
<!--添加一个遮罩层,当折叠时overflow设置为hidden,以达到只显示本周日期的目的。-->
 <div class="mask" :class="{ openmask: listIsOpen }">
      <!-- 本月日期表 -->
      <div class="monthList" ref="monthList" :class="{ listopen: listIsOpen }">
      ...
        <ul>
          <li v-for="day in monthList" :key="day.id" class="monthList-day" :class="{
            notThisMonth: day.notThisMonth,
            selectDay: day.id === highLight,
          }" @click="!day.notThisMonth && handleSelectDay(day)">
            {{ day.value }}
          </li>
        </ul>
      </div>
</template>

以上就是日历表的核心逻辑,最后完成的效果如下:

ezgif-4-56dac76133.gif

今日完成情况圆环概览图

这个图是用ECharts做的,放在这里倒不是因为它很难,而是因为我觉得这个图还挺有意思的,所以把配置贴上来分享一下。

ezgif-4-e81f87de23.gif

methods: {
    draw() {
      // 基于准备好的dom,初始化echarts实例
      var loopChart = this.$echarts.init(document.getElementById("loopChart"));
      // 绘制图表
      loopChart.setOption({
        polar: {
          radius: [120, "60%"],
        },
        color: ["#70c3f3", "#70f3f1"],
        angleAxis: {
          max: 1,
          show: false,
          startAngle: 75,
        },
        radiusAxis: {
          type: "category",
          show: false,
          data: ["今日待办事项完成率", "今日打卡完成率"],
        },
        tooltip: {},
        series: {
          type: "bar",
          data: this.rateData, //完成率数组
          barCategoryGap: "0",
          colorBy: "data",
          coordinateSystem: "polar",
          roundCap: true,
          barWidth: 10,
          showBackground: true,
        },
      });
    },
  },
  watch: {
    rateData() {
      // 更改数据时销毁图表不然控制台会显示“这里已经有一个ECharts表了”
      this.$echarts.dispose(loopChart);
      this.draw();
    }
  },
  mounted() {
      // 初始化
    if (!loopChart) this.draw();
  },

提示弹窗

很多时候当用户点击某一个按钮时,除了事件本身进行响应,还需要一个弹窗来进行反馈,来告诉用户请求是否成功与错误信息,这是用户体验中较重要的一环。而点击事件几乎处处存在,所以我需要一个全局的弹窗,在任一组件内都可进行调用。

为了实现这一效果,我使用的是Vue.extend(),对于它,官方的用法定义是:“基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。”它可以创建一个构造函数,然后通过该构造函数可以将组件挂载到指定的元素上。

实现步骤大概如下:
1.正常编写弹窗组件;
2.创建一个js文件,在其中编写编程式使用组件的逻辑代码,即Vue.extend的使用、组件挂载和相关方法、并暴露出一个函数;
3.将该函数挂载到全局;

这样就可以在任意组件调用该函数来创建提示弹窗。但是在Vue3中已经移除了extend,采用了createApp作为替代。

其他内容

  • 项目中使用了许多svg图标,于是按照网上的方式,全局封装了一个svg-icon组件,下载了svg-sprite-loader插件,实现了svg图标的优雅使用。
  • 利用cropper.js实现了图片上传时的剪裁功能,本是想将blob上传到数据库进行存储以及下载操作,但奈何每次下载下来blob都变成了json而不是图片格式,捣鼓了几天无果后,采用了一个笨方法:转为base64传给后端进行存储。
  • 关于后端,使用express以及mysql简单实现了curd操作,使用了token进行鉴权操作。配置并封装了axios以进行前后端的交互。
  • 之所以不使用时下流行的Vue3,是因为想先熟悉Vue2再去使用Vue3进行项目的开发

最后

以上就是该项目的具体情况,希望大家多多支持,喜欢的话点个小小的⭐就是对我莫大的支持,也希望掘友们能指出一些本项目的不足之处。最后的最后,感谢您的浏览。