【实战】十一、看板页面及任务组页面开发(六) —— 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
      • 13.任务组页面 (下)
      • 14.完成 popover
      • 15.开发完成,部署页面


学习内容来源: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&12

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

13.任务组页面 (下)

新建 src\screens\TaskGroup\components\createTaskGroup.js:

import React, { useEffect } from "react";
import { Button, Drawer, Form, Input, Spin } from "antd";
import { DrawerProps } from "antd/es/drawer";
import styled from "@emotion/styled";
import { ErrorBox } from "components/lib";
import { useAddTaskGroup } from "utils/taskGroup";
import { useTaskGroupsQueryKey } from "screens/TaskGroup/utils";
import { useForm } from "antd/es/form/Form";
import { useProjectIdInUrl } from "screens/ViewBoard/utils";

export const CreateTaskGroup = (
  props: Pick<DrawerProps, "visible"> & { onClose: () => void }
) => {
  const { mutate: addEpic, isLoading, error } = useAddTaskGroup(useTaskGroupsQueryKey());
  const [form] = useForm();
  const projectId = useProjectIdInUrl();

  const onFinish = async (values: any) => {
    await addEpic({ ...values, projectId });
    props.onClose();
  };

  useEffect(() => {
    form.resetFields();
  }, [form, props.visible]);

  return (
    <Drawer
      visible={props.visible}
      onClose={props.onClose}
      forceRender={true}
      destroyOnClose={true}
      width={"100%"}
    >
      <Container>
        {isLoading ? (
          <Spin size={"large"} />
        ) : (
          <>
            <h1>创建任务组</h1>
            <ErrorBox error={error} />
            <Form
              form={form}
              layout={"vertical"}
              style={{ width: "40rem" }}
              onFinish={onFinish}
            >
              <Form.Item
                label={"名称"}
                name={"name"}
                rules={[{ required: true, message: "请输入任务组名" }]}
              >
                <Input placeholder={"请输入任务组名称"} />
              </Form.Item>

              <Form.Item style={{ textAlign: "right" }}>
                <Button
                  loading={isLoading}
                  type={"primary"}
                  htmlType={"submit"}
                >
                  提交
                </Button>
              </Form.Item>
            </Form>
          </>
        )}
      </Container>
    </Drawer>
  );
};

const Container = styled.div`
  height: 80vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
`;

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

...
import { CreateTaskGroup } from "./components/createTaskGroup";

export const TaskGroupIndex = () => {
  ...
  const [epicCreateOpen, setEpicCreateOpen] = useState(false);

  ...

  return (
    <ViewContainer>
      <Row between={true}>
        <h1>{currentProject?.name}任务组</h1>
        <Button onClick={() => setEpicCreateOpen(true)} type={"link"}>
          创建任务组
        </Button>
      </Row>
      <List
        style={{ overflow: "scroll" }}
        dataSource={taskGroups}
        itemLayout={"vertical"}
        renderItem={...}
      />
      <CreateTaskGroup
        onClose={() => setEpicCreateOpen(false)}
        visible={epicCreateOpen}
      />
    </ViewContainer>
  );
};

查看页面, 尝试新增任务组,功能正常!

注意,若是发现新增或删除后页面数据没有变化,那说明新增或删除时使用的 queryKeyuseQuery 中入参不一致,导致无法正常更新缓存数据

14.完成 popover

功能实现的差不多了,接下来查漏补缺
效果图

从图中可以看到,在项目列表页中,取消或新增收藏项目,页面左上角收藏夹不会自动更新,查看代码并结合控制台发现 ProjectPopover 中没有给 useProjects 传入参数作为 queryKey,因此在缓存中 这两个地方(收藏夹和项目列表并不一致)

编辑 src\screens\ProjectList\components\ProjectPopover.tsx(使用 refetch,在 onOpenChange 中调用):

...

export const ProjectPopover = () => {
  ...
  const { data: projects, refetch } = useProjects();
  ...

  return (
    <Popover onOpenChange={() => refetch()} placement="bottom" content={content}>
      项目
    </Popover>
  );
};
...

查看页面,这样功能正常啦!

编辑 src\utils\use-users.ts(改为 react-query 的方式):

import { useHttp } from "utils/http";
import { User } from "types/User";
import { useQuery } from "react-query";

export const useUsers = (param?: Partial<User>) => {
  const client = useHttp();

  return useQuery<User[]>(["users", param], () =>
    client("users", { data: param })
  );
};

新建 src\screens\ProjectList\components\UserPopover.tsx(页面布局有一部分与 ProjectPopover 相同,可以拿过来 src\screens\ProjectList\components\ProjectPopover.tsx):

import React from "react";
import { Divider, List, Popover, Typography } from "antd";
import styled from "@emotion/styled";
import { useUsers } from "utils/use-users";

export const UserPopover = () => {
  const { data: users, refetch } = useUsers();

  const content = (
    <ContentContainer>
      <Typography.Text type={"secondary"}>组员列表</Typography.Text>
      <List>
        {users?.map((user) => (
          <List.Item key={user.id}>
            <List.Item.Meta title={user.name} />
          </List.Item>
        ))}
      </List>
      <Divider />
    </ContentContainer>
  );

  return (
    <Popover
      onVisibleChange={() => refetch()}
      placement={"bottom"}
      content={content}
    >
      <span>组员</span>
    </Popover>
  );
};

const ContentContainer = styled.div`
  min-width: 30rem;
`;

编辑 src\authenticated-app.tsx(使用 UserPopover 组件)

...
import { UserPopover } from "screens/ProjectList/components/UserPopover";

export const AuthenticatedApp = () => {
  ...
};

const PageHeader = () => {
  ...

  return (
    <Header between={true}>
      <HeaderLeft gap={true}>
        ...
        <ProjectPopover />
        <UserPopover />
      </HeaderLeft>
      <HeaderRight>...</HeaderRight>
    </Header>
  );
};

查看页面,显示正常!

15.开发完成,部署页面

按视频教程是部署到 github pages

github 上新建一个仓库,仓库名必须为 username.github.io,其中 usernamegithub 用户名。

安装依赖

npm i gh-pages -D
npm i deploy

修改 package.json(配置部署脚本)

{
  "scripts": {
    ...
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build -r git@github.com:sindu12jun/sindu12jun.github.io.git -b main"
  },
}

注意要将 git 链接替换成自己的,并指定分支(main 替代之前 master 作为 github 默认主分支)

安装好后执行:npm run deploy(predeploy 也会自动执行),打包后的文件会自动上传到 github 并生成提交记录

打开部署后的页面,成功!

但是还有个问题,github 如何判断路由是前端路由还是后端路由?

目前点到其他子页面,刷新后 显示404

接下来解决这个问题,参考 spa-github-pages: 使用GitHub Pages 托管单页应用程序

大概步骤如下:

  1. 自定义 404 页面,将当前的路由信息记录下来
  2. 携带路由信息跳转到 index
  3. 进入 index 后检查路由信息,进行还原

新建 public\404.html(配置404页面,解决强制刷新页面时,github pages当做后端路由,从而报错的问题)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Single Page Apps for GitHub Pages</title>
  <script type="text/javascript">
    // Single Page Apps for GitHub Pages
    // MIT License
    // https://github.com/rafgraph/spa-github-pages
    // This script takes the current url and converts the path and query
    // string into just a query string, and then redirects the browser
    // to the new url with only a query string and hash fragment,
    // e.g. https://www.foo.tld/one/two?a=b&c=d#qwe, becomes
    // https://www.foo.tld/?/one/two&a=b~and~c=d#qwe
    // Note: this 404.html file must be at least 512 bytes for it to work
    // with Internet Explorer (it is currently > 512 bytes)

    // If you're creating a Project Pages site and NOT using a custom domain,
    // then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).
    // This way the code will only replace the route part of the path, and not
    // the real directory in which the app resides, for example:
    // https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
    // https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe
    // Otherwise, leave pathSegmentsToKeep as 0.
    var pathSegmentsToKeep = 0;

    var l = window.location;
    l.replace(
      l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
      l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +
      l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +
      (l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
      l.hash
    );

  </script>
</head>
<body>
</body>
</html>

编辑 public\index.html(将路由信息完整的带到 index 中还原,从而实现 SPA 的效果,当然,会看到浏览器地址栏中路由的跳转,体验上稍微差了点,不过功能上已经基本无异。)

<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <title>Jira任务管理系统</title>
    <!-- Start Single Page Apps for GitHub Pages -->
    <script type="text/javascript">
      // Single Page Apps for GitHub Pages
      // MIT License
      // https://github.com/rafgraph/spa-github-pages
      // This script checks to see if a redirect is present in the query string,
      // converts it back into the correct url and adds it to the
      // browser's history using window.history.replaceState(...),
      // which won't cause the browser to attempt to load the new url.
      // When the single page app is loaded further down in this file,
      // the correct url will be waiting in the browser's history for
      // the single page app to route accordingly.
      (function(l) {
        if (l.search[1] === '/' ) {
          var decoded = l.search.slice(1).split('&').map(function(s) {
            return s.replace(/~and~/g, '&')
          }).join('?');
          window.history.replaceState(null, null,
            l.pathname.slice(0, -1) + decoded + l.hash
          );
        }
      }(window.location))
    </script>
    <!-- End Single Page Apps for GitHub Pages -->
  </head>
  <body>
    ...
  </body>
</html>

英文注释译文:该脚本检查查询字符串中是否存在重定向,将其转换回正确的 url,并使用 window.history.replaceState(…) 将其添加到浏览器的历史记录中,这不会导致浏览器尝试加载新url。当单页应用程序在这个文件中被进一步加载时,正确的 url 将在浏览器的历史记录中等待单页应用程序相应地路由。


解决方案原地址:https://github.com/rafgraph/spa-github-pages

  • 为方便访问,博主在gitee上 fork 了一份 https://gitee.com/OliverDaDa_admin/spa-github-pages

这样部署就完成啦!


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于Echarts 绘制玫瑰图 (笔记)

目录 基于js文件绘图 基于vue3绘制玫瑰图 基于js文件绘图 // 定义一个配置对象 var option {// 图例设置legend: {top: bottom},// 工具栏设置toolbox: {show: true,feature: {mark: { show: true }, // 标记工具dataView: { show: true, readOnly: false }, // 数据视图工具r…

煤矿皮带运输智能监控算法 opencv

煤矿皮带运输智能监控算法通过opencvpython深度学习算法网络模型&#xff0c;煤矿皮带运输智能监控算法实时监测皮带运输过程中的各种异常情况&#xff0c;如跑偏、撕裂、堆料异常等&#xff0c;一旦检测到异常情况&#xff0c;立即发出告警并采取相应的措施&#xff0c;以保障…

Windows系统中Apache Http服务器简单使用

1 简介 Apache HTTP服务器是一个开源的、跨平台的Web服务器软件。它由Apache软件基金会开发和维护。Apache HTTP服务器可以在多种操作系统上运行&#xff0c;如Windows、Linux、Unix等&#xff0c;并且支持多种编程语言和技术&#xff0c;如PHP、Perl、Python、Java等。…

机器学习基础16-建立预测模型项目模板

机器学习是一项经验技能&#xff0c;经验越多越好。在项目建立的过程中&#xff0c;实 践是掌握机器学习的最佳手段。在实践过程中&#xff0c;通过实际操作加深对分类和回归问题的每一个步骤的理解&#xff0c;达到学习机器学习的目的 预测模型项目模板 不能只通过阅读来掌握…

【遮天】李小曼回归,新形象无差云曦,短板竟是身材?

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析遮天 最新一集《遮天》已经更新&#xff0c;在成功卖掉段德之后&#xff0c;叶凡便离开妖帝坟冢&#xff0c;毕竟他身上拥有庞博从妖帝坟冢带出来的道经和被誉为中州至宝的绿铜 虽然这两样物品都在叶凡的苦海中&#xff0…

uniapp 开发之仿抖音,上下滑动切换视频、点击小爱心效果

效果图&#xff1a; 功能描述&#xff1a; 上下滑动视频&#xff0c;双击暂停&#xff0c;然后第一个视频再往上滑显示”已经滑到顶了“ 开始代码&#xff1a; 首先视频接口使用的公开的视频测试接口 开放API-2.0 官网展示 Swagger UI 接口文档 一…

AMEYA360:ROHM开发出适用于条码标签打印应用、超快打印速度的热敏打印头

AMEYA360&#xff1a;ROHM开发出适用于条码标签打印应用、超快打印速度的热敏打印头 全球知名半导体制造商ROHM(总部位于日本京都市)新推出两款高可靠性高速热敏打印头 “TE2004-QP1W00A(203dpi)”和“TE3004-TP1W00A(300dpi)”&#xff0c;新产品非常适用于物流和库存管理等领…

【Python自学笔记】Python好用的模块收集(持续更新...)

文章目录 日志模块钉钉机器人命令助手持续更新中,如果您有其他实用好用的模块欢迎留言...日志模块 写代码离不开日志,自定义一个理想的日志对于小白来说可能是一件很反锁的事情,就像我刚学习Python的时候自己写的一个自定义日志,为了解决这个痛点,今天就和大家分享一个可以…

Python 接口测试之接口关键字封装

引言 我们使用RF做UI自动化测试的时候&#xff0c;使用的是关键字驱动。同样&#xff0c;Python做接口自动化测试的时候&#xff0c;也可以使用关键字驱动。但是这里并不是叫关键字驱动&#xff0c;而是叫数据驱动。而接口测试的关键字是什么呢&#xff1f; 我们数据驱动的载体…

多用户商城系统常见的安全性和数据保护措施有哪些?

电子商务的迅速发展&#xff0c;越来越多的企业选择搭建多用户商城系统来扩展业务。然而&#xff0c;随之而来的是对数据安全和保护的日益关注。在选择多用户商城系统时&#xff0c;我们需要考虑一系列的安全性和数据保护措施&#xff0c;以确保商城系统的稳定性和用户数据的完…

SpringBoot + layui 框架实现一周免登陆功能

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

抢跑预制菜,双汇发展转守为攻?

懒&#xff0c;懒出新风口&#xff0c;预制菜竟成了年轻人新时代的“田螺神话”&#xff1f; 《2022年中国预制菜产业发展白皮书》数据显示&#xff0c;2022年全国预制菜的市场规模是4196亿元人民币&#xff0c;到2026年可以突破万亿大关。 预制菜的火爆显而易见&#xff0c;…

Java的类加载顺序

加载、验证、准备、解析和初始化。 加载 “加载”(Loading)阶段是“类加载”(Class Loading)过程的第一个阶段&#xff0c;在此阶段&#xff0c;虚拟机需要完成以下三件事情&#xff1a; 通过一个类的全限定名来获取定义此类的二进制字节流。将这个字节流所代表的静态存储结构…

如果应对2023年国赛

国赛倒计时一周&#xff0c;大家多看看优秀论文&#xff0c;赛前多思考&#xff0c;使大脑在活跃状态&#xff0c;更好的应对题目。 需要历年游戏论文的小伙伴可私信我&#xff0c;或关注微信公众号私信我

Rabbitmq的Shovel

Federation 具备的数据转发功能类似&#xff0c; Shovel 够可靠、持续地从一个 Broker 中的队列 ( 作为源端&#xff0c;即source)拉取数据并转发至另一个 Broker 中的交换器 ( 作为目的端&#xff0c;即 destination) 。作为源端的队列和作为目的端的交换器可以同时位于…

Debian12 Gnome环境下的办公软件安装

一、禁用Wayland&#xff0c;启用xorg 当前Debian12 默认采用Wayland来支持gnome环境&#xff0c;但是许多软件无法在该系统下显示&#xff0c;例如&#xff1a;openoffice&#xff0c;yozo-office&#xff0c;weixin&#xff0c;fcitx。所以要在gdm3的配置文件中&#xff0c;…

二叉树的构建及遍历

目录 题目题目要求示例 解答方法一、实现思路时间复杂度和空间复杂度代码 方法二、实现思路时间复杂度和空间复杂度代码 题目 二叉树的构建及遍历 题目要求 题目链接 示例 解答 方法一、 先构建二叉树&#xff0c;再中序遍历。 实现思路 按照给出的字符串创建二叉树&am…

微信8.0.41更新来了,看看有哪些变化吧

微信给我们带来了极大的方便&#xff0c;无论是日常聊天还是工作沟通&#xff0c;几乎离不开它。 时不时会给我一种熟悉的陌生感。 这个功能&#xff0c;好像我之前是没见过的。 就比如公众号信息流&#xff0c;刷着刷着就会发现&#xff0c;怎么会有看一看的信息推流会突然出现…

浅谈Lua协程和函数的尾调用

前言 虽然不经常用到协程&#xff0c;但是也不能谈虎色变。同时&#xff0c;在有些场景&#xff0c;协程会起到一种不可比拟的作用。所以&#xff0c;了解它&#xff0c;对于一些功能&#xff0c;也会有独特的思路和想法。 协程 概念 关于进程和线程的概念就不多说。 那么…

HRS--人力资源系统(Springboot+vue)--打基础升级--(六)分页查询 + 重置按钮

一&#xff1a;先弄个简单的重置按钮 1.界面设计就放在搜索框同一列的位置 2. 在点击重置按钮时&#xff0c;清空搜索框内的内容&#xff0c;同时触发一次无条件查询(这个写法有bug&#xff0c;下面会有说明) 二&#xff1a;做分页 在MyBatis中&#xff0c;有多种方法可以实现分…