当前位置: 首页 > news >正文

【实战】十一、看板页面及任务组页面开发(六) —— 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 (<Drawervisible={props.visible}onClose={props.onClose}forceRender={true}destroyOnClose={true}width={"100%"}><Container>{isLoading ? (<Spin size={"large"} />) : (<><h1>创建任务组</h1><ErrorBox error={error} /><Formform={form}layout={"vertical"}style={{ width: "40rem" }}onFinish={onFinish}><Form.Itemlabel={"名称"}name={"name"}rules={[{ required: true, message: "请输入任务组名" }]}><Input placeholder={"请输入任务组名称"} /></Form.Item><Form.Item style={{ textAlign: "right" }}><Buttonloading={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><Liststyle={{ overflow: "scroll" }}dataSource={taskGroups}itemLayout={"vertical"}renderItem={...}/><CreateTaskGrouponClose={() => 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 (<PopoveronVisibleChange={() => 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

这样部署就完成啦!


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

相关文章:

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

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…...

在 Amazon 搭建无代码可视化的数据分析和建模平台

现代企业常常会有利用数据分析和机器学习帮助解决业务痛点的需求。如制造业中&#xff0c;利用设备采集上来的数据做预测性维护&#xff0c;质量控制&#xff1b;在零售业中&#xff0c;利用客户端端采集的数据做渠道转化率分析&#xff0c;个性化推荐等。 亚马逊云科技开发者…...

Pinely Round 2 (Div. 1 + Div. 2) G. Swaps(组合计数)

题目 给定一个长度为n(n<1e6)的序列&#xff0c;第i个数ai(1<ai<n)&#xff0c; 操作&#xff1a;你可以将当前i位置的数和a[i]位置的数交换 交换可以操作任意次&#xff0c;求所有本质不同的数组的数量&#xff0c;答案对1e97取模 思路来源 力扣群 潼神 心得 感…...

elasticSearch+kibana+logstash+filebeat集群改成https认证

文章目录 一、生成相关证书二、配置elasticSearh三、配置kibana四、配置logstash五、配置filebeat六、连接https es的java api 一、生成相关证书 ps&#xff1a;主节点操作 切换用户&#xff1a;su es 进入目录&#xff1a;cd /home/es/elasticsearch-7.6.2 创建文件&#x…...

GPT带我学-设计模式-迭代器模式

1 什么是迭代器设计模式&#xff1f; 迭代器设计模式是一种行为型设计模式&#xff0c;用于提供一种统一的方式来遍历一个集合对象中的元素&#xff0c;而不需要暴露该对象的内部结构。它将集合对象的遍历操作与集合对象本身分离开来&#xff0c;使得遍历操作可以独立于集合对…...

数学建模--层次分析法(AHP)的Python实现

目录 1.算法流程简介 2.算法核心代码 3.算法效果展示 1.算法流程简介 """ AHP:层次分析法,层次分析法还是比较偏向于主观的判断的,所以在建模的时候尽可能不要去使用层次分析法 不过在某些创新的评价方法上,也是能够运用层次分析使得评价变得全面一些,有可…...

机器学习笔记之最优化理论与方法(三)凸集的简单认识(下)

机器学习笔记之最优化理论与方法——凸集的简单认识[下] 引言回顾&#xff1a;基本定义——凸集关于保持集合凸性的运算仿射变换 凸集基本性质&#xff1a;投影定理点与凸集的分离支撑超平面定理 引言 继续凸集的简单认识(上)进行介绍&#xff0c;本节将介绍凸集的基本性质以及…...

Apipost:API文档、调试、Mock与测试的一体化协作平台

随着数字化转型的加速&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中&#xff0c;API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台&#xff0c;旨在解决这些问题&#xf…...

Homebrew下载安装及使用教程

Homebrew是什么&#xff1f; 简单来说&#xff0c;就是用命令行的形式去管理mac系统的包或软件。 安装命令 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"国内请使用镜像源进行下载 执行上述命令后会要求输入…...

【Codeforces】CF193D Two Segments

题目链接 CF方向 Luogu方向 题目解法 考虑在值域上的问题&#xff1a;有多少段区间&#xff0c;对应在排列上不超过 2 2 2 段 肯定需要枚举一个端点&#xff0c;另一个快速算出&#xff0c;考虑枚举值域区间右端点 r r r&#xff0c;计算 l l l 可以发现&#xff0c;新增…...

内存管理概述

前言 在学习计算机科学时&#xff0c;内存管理是一个非常重要的概念。简单地说&#xff0c;内存是计算机用来存储和访问数据的地方。而内存管理是计算机系统如何分配、使用和管理内存的过程。 为什么要学习内存管理&#xff1f; 1. 高效性&#xff1a;内存管理能够帮助计算机更…...

Spring的重试机制-SpringRetry

在我们的日常开发中&#xff0c;经查会遇到调用接口失败的情况&#xff0c;这时候就需要通过一些方法来进行重试&#xff0c;比如通过while循环手动重复调用或&#xff0c;或者通过记录错误接口url和参数到数据库&#xff0c;然后手动调用接口&#xff0c;或者通过JDK/CGLib动态…...

水稻叶病害数据集(目标检测,yolo使用)

1.数据集文件夹 train文件夹&#xff08;44229张&#xff09;&#xff0c;test文件夹&#xff08;4741张&#xff09;&#xff0c;valid文件夹&#xff08;6000张&#xff09; 2.train文件夹展示 labels展示 标签txt展示 data.yaml文件展示 对数据集感兴趣的可以关注最后一行…...

鸿蒙系列-如何使用好 ArkUI 的 @Reusable?

如何使用好 ArkUI 的 Reusable&#xff1f; OpenHarmony 组件复用机制 在ArkUI中&#xff0c;UI显示的内容均为组件&#xff0c;由框架直接提供的称为 系统组件&#xff0c;由开发者定义的称为 自定义组件。 在进行 UI 界面开发时&#xff0c;通常不是简单的将系统组件进行组合…...

展锐平台音频框架

Audio DT介绍 1.概述 DT&#xff08;Device Tree&#xff09;是一种描述硬件的数据结构&#xff0c;DTS即设备树源码。 2.Audio DTS 文件架构 \bsp\kernel\kernel.4.14\arch\arm64\boot\sprd ums512.dts //SOC级相关节点 ——sc2730.dtsi //Codec ——sharkl5Pro.dts…...

webpack loader和plugins的区别

在Webpack中&#xff0c;Loader和Plugin是两个不同的概念&#xff0c;用于不同的目的。 Loader是用于处理非JavaScript模块的文件的转换工具。它们将文件作为输入&#xff0c;并将其转换为Webpack可以处理的模块。例如&#xff0c;当您在Webpack配置中使用Babel Loader时&…...

适配器模式:接口的平滑过渡

欢迎来到设计模式系列的第七篇文章&#xff01;在前面的几篇文章中&#xff0c;我们已经学习了一些常见的设计模式&#xff0c;今天我们将继续探讨另一个重要的设计模式——适配器模式。 适配器模式简介 适配器模式是一种结构型设计模式&#xff0c;它主要用于将一个类的接口…...

vscode搭建springboot开发环境

前言 idea好用到但是收money&#xff0c;eclipse免费但是界面有点丑&#xff0c;所以尝试使用vscode开发springboot 提前准备 安装jdk&#xff0c;jdk需要大于11 安装vscode 安装maven 安装插件 主要是下面的插件 Extension Pack for JavaSpring Boot Extension PackDepe…...

SpringMVC-学习笔记

文章目录 1.概述1.1 SpringMVC快速入门 2. 请求2.1 加载控制2.2 请求的映射路径2.3 get和post请求发送2.4 五种请求参数种类2.5 传递JSON数据2.6 日期类型参数传递 3.响应3.1 响应格式 4.REST风格4.1 介绍4.2 RESTful快速入门4.3 简化操作 1.概述 SpringMVC是一个基于Java的Web…...

【STM32】学习笔记(TIM定时器)

TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能&#xff0c;而且…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...