【Next.js 项目实战系列】07-分配 Issue 给用户
原文链接
CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧
上一篇【Next.js 项目实战系列】06-身份验证
分配 Issue 给用户
本节代码链接
Select Button
# /app/issues/[id]/AssigneeSelect.tsx"use client";
import { Select } from "@radix-ui/themes";const AssigneeSelect = () => {return (<Select.Root><Select.Trigger placeholder="Assign..." /><Select.Content><Select.Group><Select.Label>Suggestions</Select.Label><Select.Item value="1">Castamere</Select.Item></Select.Group></Select.Content></Select.Root>);
};
export default AssigneeSelect;
效果如下

获取所有用户
本节代码链接
构建 API
# /app/api/users.tsximport { NextRequest, NextResponse } from "next/server";
import prisma from "@/prisma/client";export async function GET(reques: NextRequest) {const users = await prisma.user.findMany({ orderBy: { name: "asc" } });return NextResponse.json(users);
}
客户端获取数据
# /app/issues/[id]/AssigneeSelect.tsx"use client";
import { User } from "@prisma/client";
import { Select } from "@radix-ui/themes";
import axios from "axios";
import { useEffect, useState } from "react";const AssigneeSelect = () => {const [users, setUsers] = useState<User[]>([]);useEffect(() => {const getUsers = async () => {const { data } = await axios.get<User[]>("/api/users");setUsers(data);};getUsers();}, []);return (<Select.Root><Select.Trigger placeholder="Assign..." /><Select.Content><Select.Group><Select.Label>Suggestions</Select.Label>{users.map((user) => (<Select.Item value={user.id} key={user.id}>{user.name}</Select.Item>))}</Select.Group></Select.Content></Select.Root>);
};
export default AssigneeSelect;
React-Query
配置 React-Query
本节代码链接
使用如下命令安装 React-Query
npm i @tanstack/react-query
安装好后,在 /app 目录下创建 QueryClientProvider.tsx
# /app/QueryClientProvider.tsx"use client";
import {QueryClient,QueryClientProvider as ReactQueryClientProvider,
} from "@tanstack/react-query";
import { PropsWithChildren } from "react";const queryClient = new QueryClient();const QueryClientProvider = ({ children }: PropsWithChildren) => {return (<ReactQueryClientProvider client={queryClient}>{children}</ReactQueryClientProvider>);
};
export default QueryClientProvider;
然后在 layout 中将 body 内所有内容用 QueryClientProvider 包起来
# /app/layout.tsxexport default function RootLayout({children,
}: Readonly<{children: React.ReactNode;
}>) {return (<html lang="en"><body className={inter.className}><QueryClientProvider><AuthProvider><Theme appearance="light" accentColor="violet"><NavBar /><main className="p-5"><Container>{children}</Container></main></Theme></AuthProvider></QueryClientProvider></body></html>);
}
使用 React-Query
本节代码链接
首先,在 "/app/issues/[id]/Assign" 中去掉之前的 useEffect 和 useState,之后参照下面修改
# /app/issues/[id]/AssigneeSelect.tsx...
+ import { useQuery } from "@tanstack/react-query";
+ import { Skeleton } from "@/app/components";const AssigneeSelect = () => {
+ const {
+ data: users,
+ error,
+ isLoading,
+ } = useQuery<User[]>({// 用于缓存的 key,在不同地方调用 useQuery 若 key 一样则不会重复获取
+ queryKey: ["users"],// 用于获取数据的函数
+ queryFn: () => axios.get<User[]>("/api/users").then((res) => res.data),// 数据缓存多久
+ staleTime: 60 * 1000,// 最多重复获取几次
+ retry: 3,
+ });
+ if (error) return null;
+ if (isLoading) return <Skeleton />;...};export default AssigneeSelect;
完整代码(非 git diff 版)
# /app/issues/[id]/AssigneeSelect.tsx"use client";
import { User } from "@prisma/client";
import { Select } from "@radix-ui/themes";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { Skeleton } from "@/app/components";const AssigneeSelect = () => {const {data: users,error,isLoading,} = useQuery<User[]>({queryKey: ["users"], // 用于缓存的 key,在不同地方调用 useQuery 若 key 一样则不会重复获取queryFn: () => axios.get<User[]>("/api/users").then((res) => res.data), // 用于获取数据的函数staleTime: 60 * 1000, // 数据缓存多久retry: 3, // 最多重复获取几次});if (error) return null;if (isLoading) return <Skeleton />;return (<Select.Root><Select.Trigger placeholder="Assign..." /><Select.Content><Select.Group><Select.Label>Suggestions</Select.Label>{users?.map((user) => (<Select.Item value={user.id} key={user.id}>{user.name}</Select.Item>))}</Select.Group></Select.Content></Select.Root>);
};
export default AssigneeSelect;
Prisma Relation
本节代码链接
我们需要在 Prisma 中的 Issue model 和 User model 创建一个 Relation
# schema.prismamodel Issue {id Int @id @default(autoincrement())title String @db.VarChar(255)description String @db.Textstatus Status @default(OPEN)createdAt DateTime @default(now())updatedAt DateTime @updatedAt()
+ assignedToUserId String? @db.VarChar(255)
+ assignedToUser User? @relation(fields: [assignedToUserId], references: [id])}model User {id String @id @default(cuid())name String?email String? @uniqueemailVerified DateTime?image String?accounts Account[]sessions Session[]
+ assignedIssues Issue[]}
更新修改 Issue API
本节代码链接
首先,添加一个新的 zod schema,其中 title, description, assignedToUserId 都设置为了 optional
# validationSchema.tsimport { z } from "zod";export const issueSchema = z.object({title: z.string().min(1, "Title is required!").max(255),description: z.string().min(1, "Description is required!").max(65535),
});export const patchIssueSchema = z.object({title: z.string().min(1, "Title is required!").max(255).optional(),description: z.string().min(1, "Description is required!").optional(),assignedToUserId: z.string().min(1, "AssignedToUserId is required.").max(255).optional().nullable(),
});
然后修改 "/app/api/issues/[id]/route.tsx"
# /app/api/issues/[id]/route.tsx+ import { patchIssueSchema } from "@/app/validationSchema";...export async function PATCH(request: NextRequest,{ params }: { params: { id: string } }) {const session = await getServerSession(authOptions);if (!session) return NextResponse.json({}, { status: 401 });const body = await request.json();// 换成 patchIssueSchema
+ const validation = patchIssueSchema.safeParse(body);if (!validation.success)return NextResponse.json(validation.error.format(), { status: 400 });// 直接将 title, description, assignedToUserId 结构出来
+ const { title, description, assignedToUserId } = body;// 若 body 中有 assignedToUserId,则判断该用户是否存在
+ if (assignedToUserId) {
+ const user = await prisma.user.findUnique({
+ where: { id: assignedToUserId },
+ });
+ if (!user)
+ return NextResponse.json({ error: "Invalid user" }, { status: 400 });
+ }const issue = await prisma.issue.findUnique({where: { id: parseInt(params.id) },});if (!issue)return NextResponse.json({ error: "Invalid Issue" }, { status: 404 });const updatedIssue = await prisma.issue.update({where: { id: issue.id },
+ data: {
+ title,
+ description,
+ assignedToUserId,
+ },});return NextResponse.json(updatedIssue, { status: 200 });}
分配 Issue
本节代码链接
# /app/issues/[id]/AssigneeSelect.tsx...const AssigneeSelect = ({ issue }: { issue: Issue }) => {...return (<Select.Root// 设置初始显示值
+ defaultValue={issue.assignedToUserId || ""}// 当选择时,使用patch (不需要await)
+ onValueChange={(userId) => {
+ axios.patch("/api/issues/" + issue.id, {
+ assignedToUserId: userId === "Unassign" ? null : userId,
+ });
+ }}><Select.Trigger placeholder="Assign..." /><Select.Content><Select.Group><Select.Label>Suggestions</Select.Label>{/* 添加一个 unassign */}
+ <Select.Item value="Unassign">Unassign</Select.Item>{users?.map((user) => (<Select.Item value={user.id} key={user.id}>{user.name}</Select.Item>))}</Select.Group></Select.Content></Select.Root>);};export default AssigneeSelect;
显示 Toast
本节代码链接
使用如下命令安装
npm i react-hot-toast
我们只需要在该组件任意地方添加 <Toaster /> 组件,然后在需要报错的地方调用 toast() 函数即可
# /app/issues/[id]/AssigneeSelect.tsx+ import toast, { Toaster } from "react-hot-toast";const AssigneeSelect = ({ issue }: { issue: Issue }) => {return (<><Select.RootdefaultValue={issue.assignedToUserId || ""}onValueChange={ (userId) => {
+ axios
+ .patch("/api/issues/" + issue.id, {
+ assignedToUserId: userId === "Unassign" ? null : userId,
+ })// 调用 toast.error()即可
+ .catch(() => toast.error("Changes could not be saved!"));}}>...</Select.Root>
+ <Toaster /></>);};export default AssigneeSelect;
效果如下

CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧
下一篇讲数据处理
下一篇【Next.js 项目实战系列】08-数据处理
相关文章:
【Next.js 项目实战系列】07-分配 Issue 给用户
原文链接 CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧 上一篇【Next.js 项目实战系列】06-身份验证 分配 Issue 给用户 本节代码链接 Select Button # /app/issues/[i…...
Web,RESTful API 在微服务中的作用是什么?
大家好,我是锋哥。今天分享关于【Web,RESTful API 在微服务中的作用是什么?】面试题?希望对大家有帮助; Web,RESTful API 在微服务中的作用是什么? 在微服务架构中,Web 和 RESTful …...
Ajax:跨域、防抖和节流、HTTP协议
在善意的“双向奔赴”中,每个普通人都如星辰,微小但释放着自己的光芒,交织成灿烂的星河 文章目录 跨域防抖和节流HTTP协议HTP状态码以及代表意义错误代码的影响移动的小天使 跨域 同源策略 概念:协议,域名,…...
数据结构(8.2_2)—希尔排序
希尔排序的定义: 第一趟:先将在排序表中根据增量di分别将数组元素分别插入各个子表 ,在进行排序 代码实现: 算法性能分析 稳定性:不稳定! 适用性:仅适用于顺序表,不适用于链表 总…...
Netty笔记
本笔记是看了黑马的Netty进行总结的。想要更详细的可以去看视频 学习netty之前要先打好NIO的基础,可以先去看我的另一篇文章 一、概述 不想看的可以直接跳过 Netty 的地位 Netty 在 Java 网络应用框架中的地位就好比:Spring 框架在 JavaEE 开发中的地位…...
管道燃气监管系统
一、建设目标 建立一个高效、智能、安全的管道燃气监管系统,实现对管道燃气的实时监测、风险预警、事故应急处理和数据分析,确保燃气供应的安全稳定,提高燃气管理的效率和水平。 二、系统架构 感知层 安装压力传感器、流量传感器、温度传感…...
Python语法结构(三)(Python Syntax Structure III)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...
08_Linux文件查找技巧:locate、find 和 grep 命令详解
系列文章导航:01_Linux基础操作CentOS7学习笔记-CSDN博客 文章目录 1. locate命令2. grep命令3. find命令 在Linux系统中,文件查找是一项常见的任务。本文将详细介绍三种强大的文件查找命令:locate、find 和 grep,以及它们的使用…...
JAVA 实验六
一: (1) 运行以上尟序并尣以上尟序尜尢一行尥码添加注解,将尟序保存尣e601.java (2) 以上尟序尣类变量是哪一个变量,类尠尞是哪一个尠尞,请找出类变量和类尠尞被使用尜语…...
电脑查不到IP地址是什么原因?怎么解决
在日常使用电脑的过程中,有时会遇到无法查询到电脑IP地址的情况,这可能会影响到网络的正常使用。本文将探讨电脑查不到IP地址的可能原因,并提供相应的解决方案。 一、原因分析 网络连接问题:首先,网络连接不稳定或…...
Axure重要元件三——中继器修改数据
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:中继器修改数据 主要内容:显示编辑内容、表格赋值、修改数据 应用场景:更新行、表单数据行修改 案例展示: 正文…...
应用层——电子邮件、MIME、简单网络管理协议SNMP
电子邮件 电子邮件系统采用三个主要构件组成:用户代理、邮件服务器、电子邮件所需的协议 我们可以简单的认为邮件服务器中有很多邮箱,还有用来缓存再转发邮件的缓存,发送方使用用户代理通过邮件发送协议。例如SMTP将邮件发送给发送方。 邮件服…...
我与C语言二周目邂逅vlog——8.编译和链接
C语言中的编译和链接过程详细总结 1. 概述 C 语言是一种经典的系统级编程语言,其开发过程包括多个阶段,其中最关键的就是编译和链接过程。编译和链接的理解对于掌握 C 语言程序的构建至关重要。在本篇文章中,我们将深入讲解 C 语言的编译和…...
Views Page 视图页面
下图中显示的 Views 页面允许自定义网格级别及其相应的 View。 Views (视图) 页面包含两个主要部分: 关卡设计师;请注意,其他设计器页面为在关卡设计器中选择的 View 提供设置;Properties (属性) 窗口&…...
Win10 IDEA远程连接HBase
Win10 IDEA远程连接HBase Win10 IDEA连接虚拟机中的Hadoop(HDFS) 关闭Hadoop和Hbase 如果已经关闭不需要走这一步 cd /usr/local/hbase bin/stop-hbase.sh cd /usr/local/hadoop ./sbin/stop-dfs.sh获取虚拟机的ip 虚拟机终端输入 ip a关闭虚拟机…...
1.centos 镜像
centos 它有官网的下载地址:https://vault.centos.org/ 选择想要的版本,我选择 centos7.8 进入到镜像目录 isos 选择 x86_64 选择想要的版本,我选择 CentOS-7-x86_64-DVD-2003.iso 安装就正常安装就行。我选择虚拟机安装。这个参考&…...
electron 操作 cookie
前言:在 Electron 中操作 Cookie 可以使用electron模块提供的session对象来实现。 一、获取 Cookie 通过defaultSession获取默认会话对象,然后调用cookies.get方法并传入要获取 Cookie 的 URL 地址,以获取该 URL 对应的 Cookie。 const el…...
黑马软件测试第一篇_Linux
Linux 操作系统 说明: 所有硬件设备组装完成后的第⼀一层软件, 能够使⽤用户使⽤用硬件设备的软件 即为操作系统 常见分类 桌⾯面操作系统: Windows/macOS/Linux移动端操作系统: Android(安卓)/iOS(苹果)服务器器操作系统: Linux/Windows Server嵌⼊入式操作系统: Android(底…...
npm run dev 启动前端项目的原理
在一个使用 Vite 构建工具的 Vue 项目中,当你运行 npm run dev 时,实际执行的命令是 vite。为了理解这一过程,我们需要了解几个关键点: package.json 文件中的 scripts 字段: "scripts": {"dev": "vite&…...
【2024年SCI一区新算法:黑翅鸢优化算法 】分布式电网故障定位
1 场景介绍 使用10节点网络 2 故障设置 分为单重故障和两重故障 %% 2 故障设置 %% 1)单重故障 I[1,-1,0,0,-1,-1,0,0,-1,-1]; % 区段1故障 节点状态实际编码(是否流过故障电流) % I[1,1,0,0,-1,-1,0,0,-1,-1]; % 区段2故障 % I[…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...
论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...
