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

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
    • 七、Hook,路由,与 URL 状态管理
    • 八、用户选择器与项目编辑功能
    • 九、深入React 状态管理与Redux机制
    • 十、用 react-query 获取数据,管理缓存
    • 十一、看板页面及任务组页面开发
      • 1~3
      • 4~6
      • 7&8
      • 9&10
      • 11.排序乐观更新
      • 12.任务组页面 (上)


学习内容来源: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~3

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

4~6

  • 十一、看板页面及任务组页面开发(二)

7&8

  • 十一、看板页面及任务组页面开发(三)

9&10

  • 十一、看板页面及任务组页面开发(四)

11.排序乐观更新

新建 src\utils\reorder.ts (这部分视频没有详细讲。。。之后项目中如果用到可以深挖一下):

/*** 在本地对排序进行乐观更新* @param fromId 要排序的项目的id* @param type 'before' | 'after'* @param referenceId 参照id* @param list 要排序的列表, 比如tasks, kanbans*/
export const reorder = ({fromId,type,referenceId,list,
}: {list: { id: number }[];fromId: number;type: "after" | "before";referenceId: number;
}) => {const copiedList = [...list];// 找到fromId对应项目的下标const movingItemIndex = copiedList.findIndex((item) => item.id === fromId);if (!referenceId) {return insertAfter([...copiedList], movingItemIndex, copiedList.length - 1);}const targetIndex = copiedList.findIndex((item) => item.id === referenceId);const insert = type === "after" ? insertAfter : insertBefore;return insert([...copiedList], movingItemIndex, targetIndex);
};/*** 在list中,把from放在to的前边* @param list* @param from* @param to*/
const insertBefore = (list: unknown[], from: number, to: number) => {const toItem = list[to];const removedItem = list.splice(from, 1)[0];const toIndex = list.indexOf(toItem);list.splice(toIndex, 0, removedItem);return list;
};/*** 在list中,把from放在to的后面* @param list* @param from* @param to*/
const insertAfter = (list: unknown[], from: number, to: number) => {const toItem = list[to];const removedItem = list.splice(from, 1)[0];const toIndex = list.indexOf(toItem);list.splice(toIndex + 1, 0, removedItem);return list;
};

编辑 src\utils\use-optimistic-options.ts(在之前写的配置中调用,完成乐观更新):

...
export const useReorderViewboardConfig = (queryKey: QueryKey) =>useConfig(queryKey, (target, old) => reorder({ list: old, ...target }));export const useReorderTaskConfig = (queryKey: QueryKey) =>useConfig(queryKey, (target, old) => {const orderedList = reorder({ list: old, ...target }) as Task[];return orderedList.map((item) =>item.id === target.fromId? { ...item, kanbanId: target.toKanbanId }: item);});

由于 task 的排序有可能是跨 面板 的,因此会复杂一些

查看效果,发现在拖拽到其他面板后,若是原面板为空,拖不回去了。。。因此需要为 DropChild 加一个最小高度

编辑 src\screens\ViewBoard\components\ViewboardCloumn.tsx:

...
export const ViewboardColumn = React.forwardRef<...>((...) => {...return (<Container {...props} ref={ref}>...<TasksContainer><Drop {...}><DropChild style={{minHeight: '5px'}}>...</DropChild></Drop><CreateTask kanbanId={viewboard.id} /></TasksContainer></Container>);
});
...

至此拖拽大功告成!

12.任务组页面 (上)

看板页面开发完,接下来是任务组页面

新建 src\types\TaskGroup.ts

export interface TaskGroup {id: number;name: string;projectId: number;// 开始时间start: number;// 结束时间end: number;
}

新建 src\utils\taskGroup.ts(与看板 Viewboard(kanban) 类似,可以复制后修改):

import { cleanObject } from "utils";
import { useHttp } from "./http";
import { TaskGroup } from "types/TaskGroup";
import { QueryKey, useMutation, useQuery } from "react-query";
import {useAddConfig,useDeleteConfig,
} from "./use-optimistic-options";export const useTaskGroups = (param?: Partial<TaskGroup>) => {const client = useHttp();return useQuery<TaskGroup[]>(["taskgroups", param], () =>client("epics", { data: cleanObject(param || {}) }));
};export const useAddTaskGroup = (queryKey: QueryKey) => {const client = useHttp();return useMutation((params: Partial<TaskGroup>) =>client(`epics`, {method: "POST",data: params,}),useAddConfig(queryKey));
};export const useDeleteTaskGroup = (queryKey: QueryKey) => {const client = useHttp();return useMutation((id?: number) =>client(`epics/${id}`, {method: "DELETE",}),useDeleteConfig(queryKey));
};

新建 src\screens\TaskGroup\utils.ts:

import { useProjectIdInUrl } from "screens/ViewBoard/utils";export const useTaskGroupSearchParams = () => ({projectId: useProjectIdInUrl(),
});export const useTaskGroupsQueryKey = () => ["taskgroups",useTaskGroupSearchParams(),
];

修改 src\types\Task.ts(属性字段需要和实际数据一致。。。):

export interface Task {id: number;name: string;projectId: number;processorId: number; // 经办人epicId: number; // 任务组(原 taskGroupId)kanbanId: number;typeId: number; // bug or tasknote: string;
}

编辑 src\screens\TaskGroup\index.tsx(之前新建路由时创建过,页面布局有一部分与看板相同,可以拿过来 src\screens\ViewBoard\index.tsx):

import { Row, ViewContainer } from "components/lib";
import { useProjectInUrl } from "screens/ViewBoard/utils";
import { useTaskGroups } from "utils/taskGroup";
import { useTaskGroupSearchParams, useTaskGroupsQueryKey } from "./utils";
import { Button, List, Modal } from "antd";
import dayjs from "dayjs";
import { useTasks } from "utils/task";
import { Link } from "react-router-dom";
import { TaskGroup } from "types/TaskGroup";
import { useState } from "react";export const TaskGroupIndex = () => {const { data: currentProject } = useProjectInUrl();const { data: taskGroups } = useTaskGroups(useTaskGroupSearchParams());const { data: tasks } = useTasks({ projectId: currentProject?.id });return (<ViewContainer><Row between={true}><h1>{currentProject?.name}任务组</h1><Button onClick={() => setEpicCreateOpen(true)} type={"link"}>创建任务组</Button></Row><Liststyle={{ overflow: "scroll" }}dataSource={taskGroups}itemLayout={"vertical"}renderItem={(taskGroup) => (<List.Item><List.Item.Metatitle={<Row between={true}><span>{taskGroup.name}</span><Button onClick={() => {}} type={"link"}>删除</Button></Row>}description={<div><div>开始时间:{dayjs(taskGroup.start).format("YYYY-MM-DD")}</div><div>结束时间:{dayjs(taskGroup.end).format("YYYY-MM-DD")}</div></div>}/><div>{tasks?.filter((task) => task.epicId === taskGroup.id).map((task) => (<Linkto={`/projects/${currentProject?.id}/viewboard?editingTaskId=${task.id}`}key={task.id}>{task.name}</Link>))}</div></List.Item>)}/></ViewContainer>);
};

查看页面效果,点击对应任务会跳转到看板并打开任务编辑窗口

编辑 src\screens\TaskGroup\index.tsx(新增删除任务组功能):

...
import { useDeleteTaskGroup, useTaskGroups } from "utils/taskGroup";export const TaskGroupIndex = () => {...const { mutate: deleteTaskGroup } = useDeleteTaskGroup(useTaskGroupsQueryKey());const confirmDeleteEpic = (taskGroup: TaskGroup) => {Modal.confirm({title: `确定删除项目组:${taskGroup.name}`,content: "点击确定删除",okText: "确定",onOk() {deleteTaskGroup(taskGroup.id);},});};return (<ViewContainer><Row between={true}>...</Row><Liststyle={{ overflow: "scroll" }}dataSource={taskGroups}itemLayout={"vertical"}renderItem={(taskGroup) => (<List.Item><List.Item.Metatitle={<Row between={true}><span>{taskGroup.name}</span><Button onClick={() => confirmDeleteEpic(taskGroup)} type={"link"}>删除</Button></Row>}description={...}/><div>...</div></List.Item>)}/></ViewContainer>);
};

查看页面,可以正常删除任务组(建议功能尝试放到完成创建功能之后再尝试,你懂的。。。)


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

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

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

相关文章

Python WEB框架之FastAPI

Python WEB框架之FastAPI 今天想记录一下最近项目上一直在用的Python框架——FastAPI。 个人认为&#xff0c;FastAPI是我目前接触到的Python最好用的WEB框架&#xff0c;没有之一。 之前也使用过像Django、Flask等框架&#xff0c;但是Django就用起来太重了&#xff0c;各种…

SpringBoot+mybatis+pgsql多个数据源配置

一、配置文件 jdk环境&#xff1a;1.8 配置了双数据源springbootdruidpgsql&#xff0c;application.properties配置修改如下&#xff1a; #当前入库主数据库 spring.primary.datasource.typecom.alibaba.druid.pool.DruidDataSource spring.primary.datasource.driver-class…

Python|爬虫和测试|selenium框架模拟登录示例(一)

前言&#xff1a; 上一篇文章Python|爬虫和测试|selenium框架的安装和初步使用&#xff08;一&#xff09;_晚风_END的博客-CSDN博客 大概介绍了一下selenium的安装和初步使用&#xff0c;主要是打开某个网站的主页&#xff0c;基本是最基础的东西&#xff0c;那么&#xff0c;…

如何使用CSS实现一个平滑滚动到页面顶部的效果(回到顶部按钮)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 平滑滚动到页面顶部的效果&#xff08;回到顶部按钮&#xff09;⭐ 创建HTML结构⭐ 编写CSS样式⭐ 编写JavaScript函数⭐ 添加滚动事件监听器⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右…

【golang】panic函数、recover函数以及defer语句

从panic被引发到程序终止运行的大致过程是什么&#xff1f; 大致过程&#xff1a; 某个函数中的某行代码有意无意地引发了一个panic。这时&#xff0c;初始的panic详情会被建立起来&#xff0c;并且该程序的控制权会立即从从行代码转移至调用其所属函数的那行代码上&#xff…

【原创】jmeter并发测试计划

bankQPS 创建线程组 设置并发参数 HTTP请求GET 添加HTTP请求 GET请求 查看结果树 HTTP请求 POST 添加HTTP请求 参数必须设置头信息格式&#xff1a; 添加HTTP头信息 查看结果树 可以选择&#xff0c;仅查看错误日志 汇总报告

华为数通方向HCIP-DataCom H12-821题库(单选题:81-100)

第81题 某公司新购入一台网络设备,作为网络管理员,初次配置该设备通常通过什么方式? A、FTP B、Telnet C、SNMP D、Console 口登录 答案: D 解析&#xff1a; 通常情况下&#xff0c;初次配置网络设备会通过Console口登录的方式进行。Console口是一种串口接口&#xff0c…

[Linux]进程控制

[Linux]进程控制 文章目录 [Linux]进程控制进程退出情况分类进程退出码的理解进程退出方式进程等待 进程退出情况分类 进程正常执行完成 运行结果正确运行结果错误 进程异常终止 – (进程产生错误后&#xff0c;收到了操作系统的信号) 进程退出码的理解 进程主体功能执行完毕…

提升Java开发效率:掌握HashMap的常见方法与基本原理

文章目录 前言一、概述1. 认识HashMap2. HashMap 的作用和重要性3. 简要讲解 HashMap 的基本原理和实现方式 二、了解 HashMap 创建及其的常见操作方法1. HashMap的创建2. 添加元素 put()3. 访问元素 get()4. 删除元素 remove()5. 计算大小 size()6. 迭代 HashMap for-each7.判…

Android相机-架构2

应用&#xff1a; 大概流程&#xff1a;open&#xff0c;创建session&#xff0c;发送request&#xff0c;得到captureresult CameraManager&#xff0c;获取系统服务CameraService&#xff0c;打开相机&#xff1a; 1&#xff09;openCamera&#xff1a;打开相机 获取相…

树莓派示例代码 点亮led灯 跑马灯 开关控制led 红外感应灯 超声波测距

树莓派示例代码 gpio代码 gpio介绍 GPIO&#xff08;英语&#xff1a;General-purpose input/output&#xff09;&#xff0c;通用型之输入输出的简称&#xff0c;功能类似8051的P0—P3&#xff0c;其接脚可以供使用者由程控自由使用&#xff0c;PIN脚依现实考量可作为通用输…

深度学习11:Transformer

目录 什么是 Transformer&#xff1f; Encoder Decoder Attention Self-Attention Context-Attention 什么是 Transformer&#xff08;微软研究院笨笨&#xff09; RNN和Transformer区别 Universal Transformer和Transformer 区别 什么是 Transformer&#xff1f; ​ …

tomcat版本升级

一、升级原由 因漏洞扫描发现生产环境中所使用的tomcat版本有高风险漏洞&#xff0c;评估后决定将其升级至当前系统最新版本 tomcat8.5.32升级至apache-tomcat-8.5.81.tar.gz&#xff0c;可到官网下载&#xff1a;https://archive.apache.org/dist/tomcat/tomcat-8/ 二、升级步…

习题练习 C语言(暑期第二弹)

编程能力小提升&#xff01; 前言一、表达式判断二、Assii码的理解应用三、循环跳出判断四、数字在升序数组中出现的次数五、整数转换六、循环语句的应用七、函数调用八、两个数组的交集九、C语言基础十、图片整理十一、数组的引用十二、数组的引用十三、字符个数统计十四、多数…

【考研数学】矩阵、向量与线性方程组解的关系梳理与讨论

文章目录 引言一、回顾二、梳理齐次线性方程组非齐次线性方程组 写在最后 引言 两个原因让我想写这篇文章&#xff0c;一是做矩阵题目的时候就发现这三货经常绑在一起&#xff0c;让人想去探寻其中奥秘&#xff1b;另一就是今天学了向量组的秩&#xff0c;让我想起来了之前遗留…

初阶数据结构(六)队列的介绍与实现

&#x1f493;博主csdn个人主页&#xff1a;小小unicorn&#x1f493; ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的学习足迹&#x1f69a; &#x1f339;&#x1f339;&#x1f339;关注我带你学习编程知识 栈 队列的介绍队列的概念&#xff1a;队…

H5商城公众号商城系统源码 积分兑换商城系统独立后台

网购商城系统源码 积分兑换商城系统源码 独立后台附教程 测试环境&#xff1a;NginxPHP7.0MySQL5.6thinkphp伪静态

剑指 Offer 43. 1~n 整数中 1 出现的次数

目录 ​编辑 一&#xff0c;问题描述 二&#xff0c;例子 三&#xff0c;题目接口 四&#xff0c;题目解答 1&#xff0c;暴力解法 2.规律解法 总结&#xff1a; 代码&#xff1a; 一&#xff0c;问题描述 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进…

2023高教社杯数学建模思路 - 复盘:人力资源安排的最优化模型

文章目录 0 赛题思路1 描述2 问题概括3 建模过程3.1 边界说明3.2 符号约定3.3 分析3.4 模型建立3.5 模型求解 4 模型评价与推广5 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 描述 …

异侠的CSDN笔记目录

文章目录 B站雷神B站黑马其他零散记录MyBatis 及其 Plus常用工具类线程 & 网络编程过滤器监听器Listener会话Session异步请求 AJAX B站雷神 基础知识 如何创建项目 快速创建项目 自动配置原理 容器功能 小技巧 yaml用法 web开发简单功能 数据响应与内容协商模板引擎 …