【实战】十一、看板页面及任务组页面开发(一) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十三)

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
    • 七、Hook,路由,与 URL 状态管理
    • 八、用户选择器与项目编辑功能
    • 九、深入React 状态管理与Redux机制
    • 十、用 react-query 获取数据,管理缓存
    • 十一、看板页面及任务组页面开发
      • 1.看板列表开发准备工作
      • 2.看板列表初步开发
      • 3.添加task, bug 图标


学习内容来源:React + React Hook + TS 最佳实践-慕课网


相对原教程,我在学习开始时(2023.03)采用的是当前最新版本:

版本
react & react-dom^18.2.0
react-router & react-router-dom^6.11.2
antd^4.24.8
@commitlint/cli & @commitlint/config-conventional^17.4.4
eslint-config-prettier^8.6.0
husky^8.0.3
lint-staged^13.1.2
prettier2.8.4
json-server0.17.2
craco-less^2.0.0
@craco/craco^7.1.0
qs^6.11.0
dayjs^1.11.7
react-helmet^6.1.0
@types/react-helmet^6.1.6
react-query^6.1.0
@welldone-software/why-did-you-render^7.0.1
@emotion/react & @emotion/styled^11.10.6

具体配置、操作和内容会有差异,“坑”也会有所不同。。。


一、项目起航:项目初始化与配置

  • 一、项目起航:项目初始化与配置

二、React 与 Hook 应用:实现项目列表

  • 二、React 与 Hook 应用:实现项目列表

三、TS 应用:JS神助攻 - 强类型

  • 三、 TS 应用:JS神助攻 - 强类型

四、JWT、用户认证与异步请求

  • 四、 JWT、用户认证与异步请求(上)

  • 四、 JWT、用户认证与异步请求(下)

五、CSS 其实很简单 - 用 CSS-in-JS 添加样式

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(上)

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(下)

六、用户体验优化 - 加载中和错误状态处理

  • 六、用户体验优化 - 加载中和错误状态处理(上)

  • 六、用户体验优化 - 加载中和错误状态处理(中)

  • 六、用户体验优化 - 加载中和错误状态处理(下)

七、Hook,路由,与 URL 状态管理

  • 七、Hook,路由,与 URL 状态管理(上)

  • 七、Hook,路由,与 URL 状态管理(中)

  • 七、Hook,路由,与 URL 状态管理(下)

八、用户选择器与项目编辑功能

  • 八、用户选择器与项目编辑功能(上)

  • 八、用户选择器与项目编辑功能(下)

九、深入React 状态管理与Redux机制

  • 九、深入React 状态管理与Redux机制(一)

  • 九、深入React 状态管理与Redux机制(二)

  • 九、深入React 状态管理与Redux机制(三)

  • 九、深入React 状态管理与Redux机制(四)

  • 九、深入React 状态管理与Redux机制(五)

十、用 react-query 获取数据,管理缓存

  • 十、用 react-query 获取数据,管理缓存(上)

  • 十、用 react-query 获取数据,管理缓存(下)

十一、看板页面及任务组页面开发

1.看板列表开发准备工作

之前的项目详情进入看板页的路由有个小问题,点击浏览器返回按钮回不去,原因如下:

  • 路由列表是栈结构,每访问一个路由都会 push 一个新路由进去,当点击返回,就会将上一个路由置于栈顶;而进入项目详情页(从'projects''projects/1')默认重定向子路由是看板页(projects/1/viewboard),返回上一个路由时,默认又会重定向到看板页路由。列表栈示例如下:
  • ['projects', 'projects/1', 'projects/1/viewboard']

接下来解决一下这个问题,编辑 src\screens\ProjectDetail\index.tsx (重定向标签新增属性 replace,在重定向时直接替换原路由):

...
export const ProjectDetail = () => {return (<div>...<Routes>...<Route index element={<Navigate to="viewboard" replace/>} /></Routes></div>);
};

为了方便后续类型统一调用,将 src\screens\ProjectList\components\List.tsxinterface Project 提取到 src\types 目录下

视频中 是用 WebStorm ,博主用的是 VSCode:

  • 在需要重构的变量上右击,选择重构(快捷键 Ctrl + Shift + R),选择 Move to a new file,默认同变量名的文件会创建在当前文件所在同一级目录下,其他引用位置也相应改变,涉及引用位置:
    • src\utils\project.ts
    • src\screens\ProjectList\components\SearchPanel.tsx
    • src\screens\ProjectList\components\List.tsx
  • 拖动新生成的文件到 src\types 目录下,可以看到其他引用位置也相应改变
  • 相关功能文档:TypeScript Programming with Visual Studio Code

src\screens\ProjectList\components\SearchPanel.tsxinterface User 也执行同样操作,涉及引用位置:

  • src\screens\ProjectList\components\SearchPanel.tsx
  • src\screens\ProjectList\components\List.tsx
  • src\auth-provider.ts
  • src\context\auth-context.tsx
  • src\utils\use-users.ts

看板页还需要以下两个类型,新建一下:

  • src\types\Viewboard.ts:
export interface Viewboard {id: number;name: string;projectId: number;
}
  • src\types\Task.ts
export interface Task {id: number;name: string;projectId: number;processorId: number; // 经办人taskGroupId: number; // 任务组kanbanId: number;typeId: number;      // bug or tasknote: string;
}

接下来创建数据请求的 hook:

src\utils\viewboard.ts:

import { cleanObject } from "utils";
import { useHttp } from "./http";
import { Viewboard } from "types/Viewboard";
import { useQuery } from "react-query";export const useViewboards = (param?: Partial<Viewboard>) => {const client = useHttp();return useQuery<Viewboard[]>(["viewboards", param], () =>client("kanbans", { data: cleanObject(param || {}) }));
};

src\utils\task.ts:

import { cleanObject } from "utils";
import { useHttp } from "./http";
import { Task } from "types/Task";
import { useQuery } from "react-query";export const useTasks = (param?: Partial<Task>) => {const client = useHttp();return useQuery<Task[]>(["tasks", param], () =>client("tasks", { data: cleanObject(param || {}) }));
};

2.看板列表初步开发

接下来开始开发看板列表,展示需要用到项目数据,可以提取一个从 url 获取 projectId,再用 id 获取项目数据的 hook

新建 src\screens\ViewBoard\utils.ts

import { useLocation } from "react-router"
import { useProject } from "utils/project"export const useProjectIdInUrl = () => {const { pathname } = useLocation()const id = pathname.match(/projects\/(\d+)/)?.[1]return Number(id)
}export const useProjectInUrl = () => useProject(useProjectIdInUrl())export const useViewBoardSearchParams = () => ({projectId: useProjectIdInUrl()})export const useViewBoardQueryKey = () => ['viewboards', useViewBoardSearchParams()]export const useTasksSearchParams = () => ({projectId: useProjectIdInUrl()})export const useTasksQueryKey = () => ['tasks', useTasksSearchParams()]

注意:每一个 useXXXQueryKey 都要确保返回值第一项 与后续列表请求 useXXXuseQuery 的第一个参数保持一致,否则后续增删改都无法正常自动重新请求列表,问题排查比较困难

为看板定制一个展示列组件(任务列表),供每个类型来使用

新建 src\screens\ViewBoard\components\ViewboardCloumn.tsx

import { Viewboard } from "types/Viewboard";
import { useTasks } from "utils/task";
import { useTasksSearchParams } from "../utils";export const ViewboardColumn = ({viewboard}:{viewboard: Viewboard}) => {const { data: allTasks } = useTasks(useTasksSearchParams())const tasks = allTasks?.filter(task => task.kanbanId === viewboard.id)return <div><h3>{viewboard.name}</h3>{tasks?.map(task => <div key={task.id}>{task.name}</div>)}</div>
}

编辑 src\screens\ViewBoard\index.tsx

import { useDocumentTitle } from "utils";
import { useViewboards } from "utils/viewboard";
import { useProjectInUrl, useViewBoardSearchParams } from "./utils";
import { ViewboardColumn } from "./components/ViewboardCloumn"
import styled from "@emotion/styled";export const ViewBoard = () => {useDocumentTitle('看板列表')const {data: currentProject} = useProjectInUrl()const {data: viewboards, } = useViewboards(useViewBoardSearchParams())return <div><h1>{currentProject?.name}看板</h1><ColumnsContainer>{viewboards?.map(vbd => <ViewboardColumn viewboard={vbd} key={vbd.id}/>)}</ColumnsContainer></div>;
};const ColumnsContainer = styled.div`display: flex;overflow: hidden;margin-right: 2rem;
`

通过代码可知:viewboards.map 后 ViewboardColumn 渲染多次,其中 useTasks 也同时执行多次,但是仔细看浏览器开发者工具可发现,相应请求并没有执行多次,而是只执行了一次,这是因为 react-query 的缓存机制(默认两秒内发送的多个key相同且的参数相同的请求只执行最后一次)

访问看板列表可看到如下内容且三种状态任务横向排列即为正常:

待完成
管理登录界面开发开发中
管理注册界面开发
权限管理界面开发
UI开发
自测已完成
单元测试
性能优化

3.添加task, bug 图标

任务的类型接口并不直接返回,而是只返回一个 typeId,并不能明确标识任务类型,需要单独访问接口来获取具体任务类型

新建 src\types\TaskType.ts

export interface TaskType {id: number;name: string;
}

新建 src\utils\task-type.ts

import { useHttp } from "./http";
import { useQuery } from "react-query";
import { TaskType } from "types/TaskType";export const useTaskTypes = () => {const client = useHttp();return useQuery<TaskType[]>(["taskTypes"], () =>client("tasks"));
};

将以下两个 svg 文件拷贝到 src\assets

bug.svg
在这里插入图片描述

<svg xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xlinkHref="http://www.w3.org/1999/xlink"><!-- Generator: Sketch 3.5.2 (25235) - http://www.bohemiancoding.com/sketch --><title>bug</title><desc>Created with Sketch.</desc><defs/><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"><g id="bug" sketch:type="MSArtboardGroup"><g id="Bug" sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)"><rect id="Rectangle-36" fill="#E5493A" sketch:type="MSShapeGroup" x="0" y="0" width="14" height="14" rx="2"/><path d="M10,7 C10,8.657 8.657,10 7,10 C5.343,10 4,8.657 4,7 C4,5.343 5.343,4 7,4 C8.657,4 10,5.343 10,7" id="Fill-2" fill="#FFFFFF" sketch:type="MSShapeGroup"/></g></g></g>
</svg>

task.svg
在这里插入图片描述

<svg xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" width="16px" height="16px" viewBox="0 0 16 16" version="1.1"><!-- Generator: Sketch 3.5.2 (25235) - http://www.bohemiancoding.com/sketch --><title>task</title><desc>Created with Sketch.</desc><defs/><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"><g id="task" sketch:type="MSArtboardGroup"><g id="Task" sketch:type="MSLayerGroup" transform="translate(1.000000, 1.000000)"><rect id="Rectangle-36" fill="#4BADE8" sketch:type="MSShapeGroup" x="0" y="0" width="14" height="14" rx="2"/><g id="Page-1" transform="translate(4.000000, 4.500000)" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" sketch:type="MSShapeGroup"><path d="M2,5 L6,0" id="Stroke-1"/><path d="M2,5 L0,3" id="Stroke-3"/></g></g></g></g>
</svg>

直接使用可能会有如下报错:

Compiled with problems:XERROR in ./src/assets/task.svgModule build failed (from ./node_modules/@svgr/webpack/lib/index.js):
SyntaxError: unknown file: Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can set `throwIfNamespace: false` to bypass this warning.

skety:type 这种类型的标签属性改成 sketchType 驼峰这样才能被 JSX 接受。

  • 编译有问题: ./src/assets/bug.svg 中的错误-慕课网
  • reactjs - SyntaxError: unknown: Namespace tags are not supported by default - Stack Overflow

svg 文件 修改后的源码如下:

  • bug.svg
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xlinkHref="http://www.w3.org/1999/xlink" xmlnsSketch="http://www.bohemiancoding.com/sketch/ns"><!-- Generator: Sketch 3.5.2 (25235) - http://www.bohemiancoding.com/sketch --><title>bug</title><desc>Created with Sketch.</desc><defs></defs><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketchType="MSPage"><g id="bug" sketchType="MSArtboardGroup"><g id="Bug" sketchType="MSLayerGroup" transform="translate(1.000000, 1.000000)"><rect id="Rectangle-36" fill="#E5493A" sketchType="MSShapeGroup" x="0" y="0" width="14" height="14" rx="2"></rect><path d="M10,7 C10,8.657 8.657,10 7,10 C5.343,10 4,8.657 4,7 C4,5.343 5.343,4 7,4 C8.657,4 10,5.343 10,7" id="Fill-2" fill="#FFFFFF" sketchType="MSShapeGroup"></path></g></g></g>
</svg>
  • task.svg
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg"xmlnsSketch="http://www.bohemiancoding.com/sketch/ns"><!-- Generator: Sketch 3.5.2 (25235) - http://www.bohemiancoding.com/sketch --><title>task</title><desc>Created with Sketch.</desc><defs></defs><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketchType="MSPage"><g id="task" sketchType="MSArtboardGroup"><g id="Task" sketchType="MSLayerGroup" transform="translate(1.000000, 1.000000)"><rect id="Rectangle-36" fill="#4BADE8" sketchType="MSShapeGroup" x="0" y="0" width="14" height="14" rx="2"></rect><g id="Page-1" transform="translate(4.000000, 4.500000)" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" sketchType="MSShapeGroup"><path d="M2,5 L6,0" id="Stroke-1"></path><path d="M2,5 L0,3" id="Stroke-3"></path></g></g></g></g>
</svg>

编辑 src\screens\ViewBoard\components\ViewboardCloumn.tsx(引入图标,并美化):

import { Viewboard } from "types/Viewboard";
import { useTasks } from "utils/task";
import { useTasksSearchParams } from "../utils";
import { useTaskTypes } from "utils/task-type";
import taskIcon from "assets/task.svg";
import bugIcon from "assets/bug.svg";
import styled from "@emotion/styled";
import { Card } from "antd";const TaskTypeIcon = ({ id }: { id: number }) => {const { data: taskTypes } = useTaskTypes();const name = taskTypes?.find((taskType) => taskType.id === id)?.name;if (!name) {return null;}return <img alt='task-icon' src={name === "task" ? taskIcon : bugIcon} />;
};export const ViewboardColumn = ({ viewboard }: { viewboard: Viewboard }) => {const { data: allTasks } = useTasks(useTasksSearchParams());const tasks = allTasks?.filter((task) => task.kanbanId === viewboard.id);return (<Container><h3>{viewboard.name}</h3><TasksContainer>{tasks?.map((task) => (<Card style={{marginBottom: '0.5rem'}} key={task.id}><div>{task.name}</div><TaskTypeIcon id={task.id} /></Card>))}</TasksContainer></Container>);
};export const Container = styled.div`min-width: 27rem;border-radius: 6px;background-color: rgb(244, 245, 247);display: flex;flex-direction: column;padding: .7rem .7rem 1rem;margin-right: 1.5rem;
`const TasksContainer = styled.div`overflow: scroll;flex: 1;::-webkit-scrollbar {display: none;}
`

查看效果:
效果图


部分引用笔记还在草稿阶段,敬请期待。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/92659.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NPM与外部服务的集成(上)

目录 1、关于访问令牌 1.1 关于传统令牌 1.2 关于粒度访问令牌 2、创建和查看访问令牌 2.1 创建访问令牌 在网站上创建传统令牌 在网站上创建粒度访问令牌 使用CLI创建令牌 CIDR限制令牌错误 查看访问令牌 在网站上查看令牌 在CLI上查看令牌 令牌属性 1、关于访问令…

根据二叉树创建字符串

题目:给你二叉树的根节点 root &#xff0c;请你采用前序遍历的方式&#xff0c;将二叉树转化为一个由括号和整数组成的字符串&#xff0c;返回构造出的字符串。 空节点使用一对空括号对 "()" 表示&#xff0c;转化后需要省略所有不影响字符串与原始二叉树之间的一对…

centos7安装phpipam1.4

by:铁乐与猫 date&#xff1a;2021-5-11 安装依赖 sudo yum install epel-release sudo yum install php-mcrypt安装 Apache, MySQL, PHP (LAMP) stack packages sudo yum install httpd mariadb-server php php-cli php-gd php-common php-ldap php-pdo php-pear php-snmp …

Dubbo简介

1. Dubbo是什么&#xff1f; Dubbo是一个分布式服务框架&#xff0c;致力于提供高性能和透明化的RPC远程服务调用方案&#xff0c;以及SOA服务治理方案。简单的说&#xff0c;dubbo就是个服务框架&#xff0c;如果没有分布式的需求&#xff0c;其实是不需要用的&#xff0c;只…

【计算机网络】——数据链路层

二、组帧 1、字符计数法 帧头部使用一个字符来表示帧的大小(包括第一个计数字符) &#xff08;此处一字符一个字节&#xff09; 2、字符填充收尾定界法 特定字符来定界帧的首和尾。若帧中数据段出现等同于特定字符的字符内容&#xff0c;前置一个转义字符。(类似于正则表达…

微信小程序中键盘弹起输入框自动跳到键盘上方处理

效果展示 键盘未弹起时 键盘弹起后&#xff1a; 实现方式 话就不多说了 我直接贴代码了 原理就是用你点击的输入框的底部 距离顶部的位置 减去屏幕高度除以2&#xff0c;然后设成负值&#xff0c;再将这个值给到最外层相对定位的盒子的top属性&#xff0c;这样就不会出现顶…

jvm——垃圾回收机制(GC)详解

开始之前有几个GC的基本问题 什么是GC&#xff1f; GC 是 garbage collection 的缩写&#xff0c;意思是垃圾回收——把内存&#xff08;特别是堆内存&#xff09;中不再使用的空间释放掉&#xff1b;清理不再使用的对象。 为什么要GC&#xff1f; 堆内存是各个线程共享的空间…

flutter开发实战-just_audio实现播放音频暂停音频设置音量等

flutter开发实战-just_audio实现播放音频暂停音频设置音量等 最近开发过程中遇到需要播放背景音等音频播放&#xff0c;这里使用just_audio来实现播放音频暂停音频设置音量等 一、引入just_audio 在pubspec.yaml引入just_audio just_audio: ^2.7.0在iOS上&#xff0c;video_p…

CI/CD流水线实战

不知道为什么&#xff0c;现在什么技术都想学&#xff0c;因为我觉得我遇到了技术的壁垒&#xff0c;大的项目接触不到&#xff0c;做的项目一个字辣*。所以&#xff0c;整个人心浮气躁&#xff0c;我已经得通过每天的骑行和长跑缓解这种浮躁了。一个周末&#xff0c;我再次宅在…

Docker容器基础

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、Docker概述1、docker是什么2、Docker的设计宗旨3、容器在内核中支持2种重要技术&#xff1a; 三、Docker的核心概念四、Docker相关命令1.安装依赖包2.设置阿里云…

STM32 F103C8T6学习笔记2:GPIO的认识—GPIO的基本输入输出—点亮一个LED

今日继续学习使用 STM32 F103C8T6开发板 点亮一个LED灯&#xff0c;文章提供源码&#xff0c;测试工程&#xff0c;实验效果图&#xff0c;希望我的归纳总结会对大家有帮助~ 目录 GPIO的认识与分类 &#xff1a; 引脚安排整理&#xff1a; 定时器的引脚例举&#xff1a; …

Linux:shell脚本 正则表达式与AWK

一、正则表达式 由一类特殊字符及文本字符所编写的模式&#xff0c;其中有些字符&#xff08;元字符&#xff09;不表示字符字面意义&#xff0c;而表示控制或通配的功能&#xff0c;类似于增强版的通配符功能&#xff0c;但与通配符不同&#xff0c;通配符功能是用来处理文件…

硬件产品经理:从入门到精通(新书发布)

目录 简介 新书 框架内容 相关课程 简介 在完成多款硬件产品从设计到推向市场的过程后。 笔者于2020年开始在产品领域平台输出硬件相关的内容。 在这个过程中经常会收到很多读者的留言&#xff0c;希望能推荐一些硬件相关的书籍或资料。 其实&#xff0c;笔者刚开始做硬…

【UE】Web Browser内嵌网页的使用

零、准备 1.在Edit菜单打开插件界面 搜索Web Browser并勾选&#xff0c;按提示重启引擎。 2.在资源窗口右键创建Widget Blueprint&#xff0c;并打开 3.搜索canvas panel 并拖拽到下方 4.在实验分类中找到Web Browser拖拽到Canvs Panel下 4.选中WebBrowser在右侧细节面板中…

pycharm,VSCode 几个好用的插件

pycharm Tabnine AI Code 可以在编写程序的时候为你提供一些快捷方式&#xff0c;增加编程速度 Chinese 对英文不好的程序员来说是个不错的选择&#xff0c;可以将英文状态下的pycharm变为中文版的 ChatGPT 可以跟ai聊天&#xff0c;ai可以解决你80%的问题 &#xff0c;也可以帮…

用对角线去遍历矩阵

声明 该系列文章仅仅展示个人的解题思路和分析过程&#xff0c;并非一定是优质题解&#xff0c;重要的是通过分析和解决问题能让我们逐渐熟练和成长&#xff0c;从新手到大佬离不开一个磨练的过程&#xff0c;加油&#xff01; 原题链接 用对角线遍历矩阵https://leetcode.c…

自动化安装系统(一)

系统安装过程 加载boot loader加载启动安装菜单加载内核和initrd文件加载根系统运行anaconda的安装向导 安装光盘中与安装相关的文件 安装autofs启动后会自动出现/misc目录。 在虚拟机设置中添加CD/DVD&#xff0c;使用系统ISO文件&#xff0c;登录系统后mount /dev/cdrom …

【UE4 RTS】07-Camera Boundaries

前言 本篇实现的效果是当CameraPawn移动到地图边缘时会被阻挡。 效果 步骤 1. 打开项目设置&#xff0c;在“引擎-碰撞”中&#xff0c;点击“新建Object通道” 新建通道命名为“MapBoundaries”&#xff0c;然后点击接受 2. 向视口中添加 阻挡体积 调整阻挡体积的缩放 向四…

Redis中的数据类型

Redis中的数据类型 Redis存储的是key-value结构的数据&#xff0c;其中key是字符串类型&#xff0c;value有5种常用的数据类型: 字符串string哈希hash列表list集合set有序集合sorted set

【vue】简洁优雅的火花线、趋势线

来由 在github发现个好看易用的vue趋势线组件&#xff0c;特此记录。 效果 趋势图生成后效果如上&#xff0c;线条为渐变色&#xff0c;可设置是否平滑。具体线条走势&#xff0c;根据数据动态生成。 使用 安装 npm i vuetrend -S 引入 import Vue from "vue"…