【Next.js 项目实战系列】03-查看 Issue
原文链接
CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧
上一篇【Next.js 项目实战系列】02-创建 Issue
查看 Issue
展示 Issue
本节代码链接
首先使用 prisma 获取所有的 issues,然后添加一个 Radix UI 中的 Table 组件
# /app/issues/page.tsximport { Button, Table, TableColumnHeaderCell } from "@radix-ui/themes";import Link from "next/link";
+ import prisma from "@/prisma/client";const IssuesPage = async () => {
+ const issues = await prisma.issue.findMany();return (<div><div className="mb-5"><Button><Link href="/issues/new">New Issue</Link></Button></div>{/* Radix UI 中的 Table 组件 */}
+ <Table.Root variant="surface">
+ <Table.Header>
+ <Table.Row>
+ <TableColumnHeaderCell>Issue</TableColumnHeaderCell>
+ <TableColumnHeaderCell>Status</TableColumnHeaderCell>
+ <TableColumnHeaderCell>Created</TableColumnHeaderCell>
+ </Table.Row>
+ </Table.Header>
+ <Table.Body>
+ {issues.map((issue) => (
+ <Table.Row key={issue.id}>
+ <Table.Cell>{issue.title}</Table.Cell>
+ <Table.Cell>{issue.status}</Table.Cell>
+ <Table.Cell>{issue.createdAt.toDateString()}</Table.Cell>
+ </Table.Row>
+ ))}
+ </Table.Body>
+ </Table.Root></div>);};export default IssuesPage;
然后我们可以给不同的列添加显示选项,以适配不同的屏幕大小
# /app/issues/page.tsx...
const IssuesPage = async () => {...return (<div>...<Table.Root variant="surface"><Table.Header><Table.Row><TableColumnHeaderCell>Issue</TableColumnHeaderCell><TableColumnHeaderCell className="hidden md:table-cell">Status</TableColumnHeaderCell><TableColumnHeaderCell className="hidden md:table-cell">Created</TableColumnHeaderCell></Table.Row></Table.Header><Table.Body>{issues.map((issue) => (<Table.Row key={issue.id}><Table.Cell>{issue.title}<div className="block md:hidden">{issue.status}</div></Table.Cell><Table.Cell className="hidden md:table-cell">{issue.status}</Table.Cell><Table.Cell className="hidden md:table-cell">{issue.createdAt.toDateString()}</Table.Cell></Table.Row>))}</Table.Body></Table.Root></div>);
};
export default IssuesPage;
显示效果如下
制作 Badge
本节代码链接
在 Prisma 中添加的 Model 会自动为我们生成 Type ,方便拿来做 Interface
这里有一些技巧。首先,对于一些固定值的映射(比如这里 Issue 状态对 Badge 颜色/内容的映射),我们可以使用一个 Record 来记录,其本质为一个键值对,我们可以使用 < >
来定义键和值的数据类型
# /app/components/IssueStatusBadge.tsximport { Status } from "@prisma/client";
import { Badge } from "@radix-ui/themes";const statusMap: Record<Status,{ label: string; color: "green" | "violet" | "red" }
> = {OPEN: { label: "Open", color: "green" },IN_PROGRESS: { label: "In Progress", color: "violet" },CLOSED: { label: "Closed", color: "red" },
};const IssueStatusBadge = ({ status }: { status: Status }) => {return (<Badge color={statusMap[status].color}>{statusMap[status].label}</Badge>);
};
export default IssueStatusBadge;
最终效果如下
Loading Skeleton
本节代码链接
本节我们想要实现一个如下的加载动画
安装 delay
包用于模拟网速较慢情况,react-loading-skeleton
包用于添加骨架动画
npm i delay
npm i react-loading-skeleton
首先,我们应该在页面中把不需要加载的部分(指不需要从外部获取数据的部分,一些写死的 Text, Link, Button 之类的)封装起来,
# /app/issues/IssueAction.tsximport { Button } from "@radix-ui/themes";
import Link from "next/link";const IssueActions = () => {return (<div className="mb-5"><Button><Link href="/issues/new">New Issue</Link></Button></div>);
};
export default IssueActions;
然后在 page.tsx
同目录下创建 loading.tsx
(注意文件名必须是这个,大小写也不能改)。将 page.tsx 中 return 的内容都复制到里面,把需要加载的字段换为 Skeleton 标签即可
# /app/issues/loading.tsximport { Table, TableColumnHeaderCell } from "@radix-ui/themes";// import Skeleton
+ import Skeleton from "react-loading-skeleton";
+ import "react-loading-skeleton/dist/skeleton.css";import IssueActions from "./IssueActions";const LoadingIssuesPage = () => {// 显示 5 行 skeleton
+ const issues = [1, 2, 3, 4, 5];return (...<Table.Body>{issues.map((issue) => ({/* 将所有需要数据的字段换为 <Skeleton />即可 */}
- <Table.Row key={issue.id}>
+ <Table.Row key={issue}><Table.Cell>
- {issue.title}
+ <Skeleton /><div className="block md:hidden">
- <IssueStatusBadge status={issue.status} />
+ <Skeleton /></div></Table.Cell><Table.Cell className="hidden md:table-cell">
- <IssueStatusBadge status={issue.status} />
+ <Skeleton /></Table.Cell><Table.Cell className="hidden md:table-cell">
- {issue.createdAt.toDateString()}
+ <Skeleton /></Table.Cell></Table.Row>))}</Table.Body>...);};export default LoadingIssuesPage;
我们可以在 page.tsx 中添加一个 delay(2000)
来模拟
# /app/issues/page.tsx...
const IssuesPage = async () => {const issues = await prisma.issue.findMany();await delay(2000);...
Issue Detail Page
本节代码链接
首先创建一个页面用于展示 Issue 细节 /app/issues/[id]/page.tsx
# /app/issues/[id]/page.tsximport prisma from "@/prisma/client";
import { notFound } from "next/navigation";
interface Props {params: { id: string };
}
const IssueDeatilPage = async ({ params }: Props) => {// 判断 url 中的 id 是不是 number,比如 'issues/abc' 就直接404if (typeof params.id !== "number") notFound();// 获取 issueconst issue = await prisma.issue.findUnique({where: { id: parseInt(params.id) },});// 如果 issue 不存在,也404if (!issue) notFound();return (<div><p>{issue.title}</p><p>{issue.description}</p><p>{issue.status}</p><p>{issue.createdAt.toDateString()}</p></div>);
};
export default IssueDeatilPage;
然后在 /app/issues/page.tsx
中渲染表格时,添加一个 Link,用于跳转到 detail 页面
# /app/issues/page.tsxconst IssuesPage = async () => {return (...<Table.Cell>
+ <Link href={`/issues/${issue.id}`}>{issue.title}</Link><div className="block md:hidden"><IssueStatusBadge status={issue.status} /></div></Table.Cell>...);};
最后,我们应该为 "/app/issues" 下的每一个 page
都提供一个 loading.tsx
,否则刚刚的 "/app/issues/loading.tsx" 会应用到 "/app/issues" 下的所有页面
添加样式
本节代码链接
此处大量使用了 Radix UI 中的组件
# /app/issues/[id]/page.tsxconst IssueDeatilPage = async ({ params }: Props) => {...return (<div><Heading as="h2">{issue.title}</Heading><Flex gap="3" my="5"><IssueStatusBadge status={issue.status}></IssueStatusBadge><Text>{issue.createdAt.toDateString()}</Text></Flex><Card>{issue.description}</Card></div>);
};
效果如下
MarkDown 渲染
本节代码链接
首先,安装以下两个 package
npm i react-markdown
npm install -D @tailwindcss/typography
在 "/app/issues/[id]/page.tsx" 中,将 issue.description 用 ReactMarkdown
组件包起来即可
# /app/issues/[id]/page.tsxconst IssueDeatilPage = async ({ params }: Props) => {...return (...
- <Card>{issue.description}</Card>
+ <Card className="prose">
+ <ReactMarkdown>{issue.description}</ReactMarkdown>
+ </Card>...);};
在 Tailwind 中,默认 h1, h2, ul, ol, strong 这些标签都是无样式的,我们需要手动进行设置。刚刚安装的 tailwindcss/typography
就是这个作用。首先,在 tailwind.config.ts
中,添加 plugin,然后在需要用到这些样式的 container 的 className
中添加 prose
即可
# tailwind.config.tsimport type { Config } from "tailwindcss";const config: Config = {content: ["./pages/**/*.{js,ts,jsx,tsx,mdx}","./components/**/*.{js,ts,jsx,tsx,mdx}","./app/**/*.{js,ts,jsx,tsx,mdx}",],theme: {extend: {backgroundImage: {"gradient-radial": "radial-gradient(var(--tw-gradient-stops))","gradient-conic":"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",},},},// 在这里添加 @tailwindcss/typography"
+ plugins: [require("@tailwindcss/typography")],};export default config;
最终实现效果如下
自定义 Link Component
本节代码链接
我们想要同时应用 Next.js 中 Link 的客户端导航功能,和 Radix UI 中 Link 的样式,就可以进行如下改装,后期直接使用即可
# /app/components/link.tsximport NextLink from "next/link";
import { Link as RadixLink } from "@radix-ui/themes";interface Props {href: string;children: string;
}const Link = ({ href, children }: Props) => {return (<NextLink href={href} passHref legacyBehavior><RadixLink>{children}</RadixLink></NextLink>);
};
export default Link;
效果如下,其中 Link 的颜色会随着 Theme 的改变而改变
Loading Skeletons
本节代码链接
# /app/issues/[id]/loading.tsximport IssueStatusBadge from "@/app/components/IssueStatusBadge";
import { Box, Card, Flex, Heading } from "@radix-ui/themes";
import ReactMarkdown from "react-markdown";
import Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";const LoadingIssueDetailPage = () => {return (<Box className="max-w-xl"><Skeleton /><Flex gap="3" my="5"><Skeleton width="5rem" /><Skeleton width="8rem" /></Flex><Card className="prose"><Skeleton count={3} /></Card></Box>);
};
export default LoadingIssueDetailPage;
# /app/issues/new/loading.tsximport { Box } from "@radix-ui/themes";
import Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";const LoadingNewIssuePage = () => {return (<Box className="max-w-xl"><Skeleton /><Skeleton height="20rem" /></Box>);
};
export default LoadingNewIssuePage;
显示效果如下
动态导入(关闭 SSR)
本节代码链接
SSR(Server Side Render) 相关内容可参考组件的渲染
# /app/issues/new/page.tsx- import SimpleMDE from "react-simplemde-editor";
+ import dynamic from "next/dynamic";+ const SimpleMDE = dynamic(() => import("react-simplemde-editor"), {
+ ssr: false,
+ });
整理 imports
本节代码链接
我们可以在 Components 文件夹下添加 index.ts
,将该文件夹下所有组件都注册到其中
# /app/components/index.tsexport { default as Link } from "./Link";
export { default as ErrorMessage } from "./ErrorMessage";
export { default as IssueStatusBadge } from "./IssueStatusBadge";
export { default as Spinner } from "./Spinner";
export { default as Skeleton } from "./Skeleton";
然后在其他页面,直接使用以下 import 语句即可
import { ErrorMessage, Spinner } from "@/app/components";
CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧
下一篇讲修改 Issue
下一篇【Next.js 项目实战系列】04-修改 Issue
相关文章:
【Next.js 项目实战系列】03-查看 Issue
原文链接 CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧 上一篇【Next.js 项目实战系列】02-创建 Issue 查看 Issue 展示 Issue 本节代码链接 首先使用 prisma 获取所有…...
Android Settings 设置项修改
Settings 设置项 在 Android 系统上,WRITE_SETTINGS 这个权限从 API 1 就已经开始有了。 通过在 app 中设置权限 android.permission.WRITE_SETTINGS 允许 app 读/写 系统设置。 在官方文档的描述中,还有一段注意事项: Note: If the app targets API level 23 or higher,…...
Windows远程桌面到Ubuntu
在Ubuntu系统中,默认情况下root账户是被禁用的,为了安全起见,建议不要直接使用root账户登录图形界面。但是,如果出于特定的管理或维护需求,您可以按照以下步骤启用和使用root账户登录图形界面: 启用root账户…...
解释 RESTful API,以及如何使用它构建 web 应用程序(AI)
RESTful API(Representational State Transfer)是一种基于HTTP协议的软件架构风格,用于构建可扩展、可维护和可重用的网络服务。 RESTful API的特点包括: 1. 基于资源:每个API都代表一个或多个资源,这些资…...
NestJs:处理身份验证和授权
使用 Nest.js 开发项目时,处理身份验证和授权是常见的需求,可以采用以下架构和实现方式。 架构 用户认证模块 (Auth Module): 服务 (Service): 处理用户登录逻辑,生成 JWT(JSON Web Token),以及验证 token…...
Java EE规范
1、简介 Java EE的全称是Java Platform, Enterprise Edition。早期Java EE也被称为J2EE,即Java 2 Platform Enterprise Edition的缩写。从J2EE1.5以后,就改名成为Java EE。一般来说,企业级应用具备这些特征:1、数据量特别大&…...
Ollama及其Open-WebUI部署更新
目录 1 安装ollama 2 安装Open-WebUI 2.1 不使用容器安装open-webui 2.2 使用Docker安装open-webui 2.3 基于docker升级open-webui 1 安装ollama curl -fsSL https://ollama.com/install.sh | sh启动、关闭ollama systemctl start ollama systemctl stop ollama sys…...
手写 | 设计模式
这里写目录标题 观察者 vs 发布订阅 观察者 vs 发布订阅 参考代码 观察者模式,一对多,两个角色:观察者observer和被观察者/主题Subject。 Subject维护一个数组,记录有哪些Observer;通过调自身的noticefy方法…...
基于深度学习的地形分类与变化检测
基于深度学习的地形分类与变化检测是遥感领域的一个关键应用,利用深度学习技术从卫星、无人机等地球观测平台获取的遥感数据中自动分析地表特征,并识别地形的变化。这一技术被广泛应用于城市规划、环境监测、灾害预警、土地利用变化分析等领域。 1. 地形…...
进程、线程、协程
文章目录 前言一、易混概念1.1 同步vs异步1.2 并发vs并行 二、进程(Process)2.1进程概念2.2 进程三个基本状态2.3多进程方式编程 三、线程(Thread)3.1 线程的引入3.2 线程概念3.3 多线程编程3.4 GIL对多线程的影响3.5 GIL是否意味…...
嵌入式工程师成长之路(1)——元件基础(完整版)
系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 前言一、认识元件①、认识元件②、认识封装二、电阻1.上拉电阻与下拉电阻①、定义②、应用③、阻值选择④、因上下拉电…...
在Ubuntu 20.04 上安装 CoppeliaSim
在 Ubuntu 20.04 上安装 CoppeliaSim Edu V4.6.0 rev18 的步骤如下: 1. 下载安装文件: 首先,确保您已经下载了 CoppeliaSim_Edu_V4_6_0_rev18_Ubuntu20_04.tar.xz 文件。您可以从 Coppelia Robotics 的官方网站下载。 2. 解压缩文件: 打开终端&#…...
pulseaudio的相关操作(二)
这篇文章主要介绍pulseaudio playback的相关API,pulseaudio playback的具体实例可以参考[2]。如果用pulseaudio实现playback,简单地说就是创建一个playback stream,然后指定这个stream的sink,再定期的向这个stream中写数据。 mai…...
Selenium自动化测试工具
一 .Selenium简介 是一个用于Web应用程序测试的工具 Selenium的核心功能之一是测试软件在不同浏览器和操作系统上的兼容性,确保软件功能与用户需求的一致性,提升用户体验。 自动化脚本生成与执行 Selenium支持自动录制用户操作并生成多种编程语言的测…...
优化UVM环境(九)-将interface文件放在env pkg外面
书接上回: 优化UVM环境(八)-整理project_common_pkg文件 My_env_pkg.sv里不能包含interface,需要将my_intf.sv文件放在pkg之外...
mysql 主从安装
登录看第二篇 WINDOWS系统搭建MYSQL 8.0主从模式_windows mysql8.0.34主从配置-CSDN博客 Windows下MySQL8.0最新版本超详细安装教程_windowsserver安装mysql8.0-CSDN博客 启动两个服务 可执行文件路径一致问题解决: windows,同一台机器安装两个mysq…...
【C++刷题】力扣-#121-买卖股票的最佳时机
题目描述 给定一个数组 prices,其中 prices[i] 表示第 i 天的股票价格。假设你可以在第 i 天买入并在第 j 天卖出股票(i ≤ j),设计一个算法来计算你所能获取的最大利润。注意你只能持有一股股票,并且你不能同时参与多…...
Python量化交易(二):金融市场的基础概念
引言 大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年10月学习赛的Python量化交易学习总结文档;在现代社会中,投资已成为个人、机构和政府追求财富增长和资源配置的重要方式。…...
Java方法的递归调用
Java中的方法可以通过调用自身来实现递归调用。 递归调用在解决一些问题时非常有用,特别是那些可以分解为相同结构的子问题的情况。递归调用可以让问题的解决过程更加简洁和优雅。 下面是一个简单的示例,展示了如何使用递归调用来计算一个数字的阶乘&a…...
JavaScript 第30章:综合项目
看起来您想要了解如何在一个JavaScript为主的项目中进行项目规划、技术选型、开发流程以及维护等方面的内容,并且希望结合Java的源代码来进行详细的讲解。不过,JavaScript和Java是两种不同的编程语言,通常它们的应用场景也不同。JavaScript 主…...
GB/T28181-2022规范解读、应用场景和技术实现探究
GB/T28181-2022和GB/T28181-2016区别 GB/T28181-2022《公共安全视频监控联网系统信息传输、交换、控制技术要求》与 GB/T28181-2016 相比,主要有以下区别: 术语和定义方面: 术语删减:GB/T28181-2022 删除了 “联网系统信息”“数…...
Docker容器间链路管理
Docker容器是一个轻量级的、可移植的软件打包技术,它允许开发者将应用程序及其依赖项打包到一个独立的容器中,然后发布到任何支持Docker的环境中运行。容器是完全使用沙箱机制,相互之间不会有任何接口,容器性能开销极低。 可以将…...
python画图|在三维空间的不同平面上分别绘制不同类型二维图
【1】引言 前序已经完成了基础的二维图和三维图绘制教程探索,可直达的链接包括但不限于: python画图|3D参数化图形输出-CSDN博客 python画三角函数图|小白入门级教程_正余弦函数画图python-CSDN博客 在学习过程中,发现一个案例࿱…...
与ai一起作诗(《校园清廉韵》)
与ai对话犹如拷问自己的灵魂,与其说ai助力还不如说在和自己对话。 (笔记模板由python脚本于2024年10月19日 19:18:33创建,本篇笔记适合喜欢python和诗歌的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网:https://www.python.org/ Free&…...
python matplotlib
一、图形函数 函数名称描述Bar绘制条形图Barh绘制水平条形图Boxplot绘制箱型图Hist绘制直方图his2d绘制2D直方图Pie绘制饼状图Plot在坐标轴上画线或者标记Polar绘制极坐标图Scatter绘制x与y的散点图Stackplot绘制堆叠图Stem用来绘制二维离散数据绘制(又称为火柴图&…...
秋招面试题记录_半结构化面试
c八股(可能问的多一点) 1.简单说说C11语法特性 答: 1.auto以及decltype自动类型推导,避免手动声明复杂类型,减少冗长代码提升了可读性和安全性。 2.智能指针 自动释放内存 (具体说说) 有shared和unique 差异主要体现在所有权、内存开销、…...
Java项目-基于springboot框架的疫苗接种管理系统项目实战(附源码+文档)
作者:计算机学长阿伟 开发技术:SpringBoot、SSM、Vue、MySQL、ElementUI等,“文末源码”。 开发运行环境 开发语言:Java数据库:MySQL技术:SpringBoot、Vue、Mybaits Plus、ELementUI工具:IDEA/…...
Android 12.0进程保活白名单功能实现
在Android 12.0系统中,实现进程保活白名单功能是为了确保某些重要的应用程序即使进入后台也能长时间保持运行状态,不被系统自动杀死。这一功能的实现涉及多个核心类和文件,以下是具体的实现步骤和核心功能分析: 一、实现步骤 …...
vscode 功能、设置备忘
2024年10月18日 crtl p 按文件名搜索,输入> 开始搜索命令 设置文件显示过滤和搜索过滤: ctrlp 输入 >settings 选择Preferences:Open Settings(UI),搜索exclude 配置 Files Exclude 修改显示过滤 配置 Search Exclude 修…...
错误 Failed to connect to xx.xx.xx.xx port xx: No route to host
Failed to connect to xx.xx.xx.xx port xx: No route to host 系统环境: Oracle Cloud(OCI)Ubuntu20.4 问题: 连接本机IP正常访问,连接内网ip可正常访问,但连接外网IP报错:Failed to conne…...
委托别人做网站_域名所有权/做seo要投入什么
int vac3; int main() { int vac10; ::vac; cout<<::vac<<endl; cout<<vac<<endl; } 结果为4 10 。 ::作用区域符,指明一个函数/数据属于哪个类。 ::可以不跟类名,表示全局数据或者函数(即…...
asp.net mvc 手机网站/杭州千锋教育地址
ReentrantLock: 表示重入锁,它是唯一一个实现了 Lock 接口的类。重入锁指的是线程在获得锁之后,再次获取该锁不需要阻塞,而直接关联一次计数器增加重入次数 ReentrantLock 重入锁 重入锁,表示支持重新进入的锁&…...
武汉科技公司排名/东莞优化网站关键词优化
:2014年继续教育心得体会,我认真参加了教师继续教育,学习了《中小学教师信息技术与学科教学融合》、《师者之魂》、《教师积极心理健康的培,较好的完成了本年度的继续教育学习任务,教师专业是动态发展的、终身持续的过程,现代教学应是建立在教师学习基础上的,必须在加强和发展教…...
赣州网站seo/南京seo优化
https://download.csdn.net/download/weixin_57836618/72364964...
linux wordpress伪静态/资源搜索
说明:该文章主要是以媳妇的视角 讲述 媳妇与婆婆 好好相处的一些原则。 不和公公婆婆争老公,也不要经常问:我和你妈谁在你心里更重要?我和你妈掉水里了你先救谁? 我和你妈都生气了你先哄谁?。。。等等一系列…...
前端做项目有哪些网站/行业关键词词库
说明:只是自己的学习笔记,仅供自己学习 geopandas库是利用python读取和处理gis数据的库,是用Python进行交通大数据分析比较核心的库。 各个包大全网址:https://www.lfd.uci.edu/~gohlke/pythonlibs/ geopandas官方安装入门&…...