echarts 实现签到记录日历组件

以下笔记来源:编程导航

分析

有三种基本图表可以选择:

  1. 基础日历图:https://echarts.apache.org/examples/zh/editor.html?c=calendar-simple
  2. 日历热力图:https://echarts.apache.org/examples/zh/editor.html?c=calendar-heatmap

跟上一个图的区别就是鼠标放上去可以展示具体的热力值,热力值越高,图块的颜色越深。

  1. 日历图:https://echarts.apache.org/examples/zh/editor.html?c=calendar-charts

当我们只需要实现签到功能的时候,不涉及热力数值的区分(只有 0 和 1 签到 / 未签到的区别)。

官方生成数据的循环代码如下:

for (let time = date; time <= end; time += dayTime) {
  data.push([
    echarts.time.format(time, '{yyyy}-{MM}-{dd}', false),
    Math.floor(Math.random() * 10000)
  ]);
}

得到的数据是一个二维数组,每个元素表示一个日期和对应的数值(也正是后端需要返回的结构):

[
  ['2017-01-01', 3456],
  ['2017-01-02', 8975],
  ...
]

调整热力值的范围,从而控制颜色深浅。还支持调整颜色:

visualMap: {
  show: false,
    min: 0,
    max: 1,
    inRange: {
    color: ['#efefef', 'lightgreen']  // 颜色从灰色到浅绿色
  },
},

在这里插入图片描述

实现

安装 ECharts:https://echarts.apache.org/zh/index.html

和 React ECharts 可视化库:https://github.com/hustcc/echarts-for-react

npm install --save echarts
npm install --save echarts-for-react

安装失败的话,在命令后加 --force

封装日历图组件:

1)参考 React ECharts 的 官方文档 来使用 ECharts 组件,把 Demo 代码复制到新建的组件文件中。

2)定义签到日期数组变量,将数组转换为图表需要的数据。其中,对日期的处理需要用到 dayjs 库:

tsx
复制代码
// 签到日期列表([1, 200],表示第 1 和第 200 天有签到记录)
const [dataList, setDataList] = useState<number[]>([]);

// 计算图表需要的数据
const year = new Date().getFullYear();
const optionsData = dataList.map((dayOfYear, index) => {
  // 计算日期字符串
  const dateStr = dayjs(`${year}-01-01`)
    .add(dayOfYear - 1, "day")
    .format("YYYY-MM-DD");
  return [dateStr, 1];
});

4)参考 Echarts 的官方 Demo 开发前端日历图:https://echarts.apache.org/examples/zh/editor.html?c=calendar-simple

先在 Demo 页面里调整好效果,得到 options 选项。

💡 小技巧:可以通过配置项或者询问 AI 得到需要的配置

import React, {useEffect, useState} from "react";
import ReactECharts from "echarts-for-react";
import "./index.css";
import dayjs from "dayjs";
import {getUserSignInRecordUsingGet} from "@/api/userController";
import {message} from "antd";

/**
 * 日历图组件
 * @constructor
 */
export default function CalendarChart() {
  // 签到日期列表([1, 200],表示第 1 和第 200 天有签到记录)
  const [dataList, setDataList] = useState<number[]>([1, 200]);

  const fetchDataList =  async  () => {
    try {
      // 请求后端获取数据
      const res = await getUserSignInRecordUsingGet({
        year
      });
      setDataList(res.data);
    } catch (e) {
      message.error(`获取刷题签到记录失败: ${e.message}`);
    }
  }

  useEffect(() => {
    fetchDataList();
  }, []);

  // 计算图表需要的数据
  // 当前年份
  const year = new Date().getFullYear();
  const optionsData = dataList.map((dayOfYear, index) => {
    // 计算日期字符串
    const dateStr = dayjs(`${year}-01-01`)
      .add(dayOfYear - 1, "day")
      .format("YYYY-MM-DD");
    return [dateStr, 1];
  });

  // 图表配置
  const options = {
    visualMap: {
      show: false,
      min: 0,
      max: 1,
      inRange: {
        // 颜色从灰色到浅绿色
        color: ["#efefef", "lightgreen"],
      },
    },
    calendar: {
      range: year,
      left: 20,
      // 单元格自动宽度,高度为 16 像素
      cellSize: ['auto', 16],
      yearLabel: {
        position: "top",
        formatter: `${year} 年刷题记录`,
      }
    },
    series: {
      type: "heatmap",
      coordinateSystem: "calendar",
      data: optionsData,
    },
  };

  return <ReactECharts option={options} />;
}

执行签到:

这里单独封装了一个钩子,因为进入详情页面时才会执行自动签到,而详情页面是在服务端渲染(本项目),获取不到登录态,所以我们需要单独封装一个钩子,在客户端额外发送请求来执行签到。

import { useEffect, useState } from "react";
import { message } from "antd";
import { addUserSignInUsingPost } from "@/api/userController";

/**
 * 添加用户签到记录钩子
 */
const useAddUserSignInRecord = () => {
  const [loading, setLoading] = useState(false);

  // 请求后端执行签到
  const doFetch = async () => {
    setLoading(true);
    try {
      await addUserSignInUsingPost();
    } catch (e) {
      message.error("添加刷题签到记录失败," + e.message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    doFetch();
  }, []);

  return { loading };
};

export default useAddUserSignInRecord;

该钩子需要在客户端组件中执行,因为用到了 useEffect 防止重复请求、并且还需要获取到用户登录态。

todo: 签到成功,可以保存到 LocalStorage 等位置,防止每次刷题都重复发送签到请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秀秀_heo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值