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

【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;

效果如下

Select Button

获取所有用户​

本节代码链接

构建 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;

效果如下

Toast

CSDN 的排版/样式可能有问题,去我的博客查看原文系列吧,觉得有用的话,给我的库点个star,关注一下吧 

下一篇讲数据处理

下一篇【Next.js 项目实战系列】08-数据处理

相关文章:

【Next.js 项目实战系列】07-分配 Issue 给用户

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】06-身份验证 分配 Issue 给用户 本节代码链接 Select Button​ # /app/issues/[i…...

Web,RESTful API 在微服务中的作用是什么?

大家好&#xff0c;我是锋哥。今天分享关于【Web&#xff0c;RESTful API 在微服务中的作用是什么&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; Web&#xff0c;RESTful API 在微服务中的作用是什么&#xff1f; 在微服务架构中&#xff0c;Web 和 RESTful …...

Ajax:跨域、防抖和节流、HTTP协议

在善意的“双向奔赴”中&#xff0c;每个普通人都如星辰&#xff0c;微小但释放着自己的光芒&#xff0c;交织成灿烂的星河 文章目录 跨域防抖和节流HTTP协议HTP状态码以及代表意义错误代码的影响移动的小天使 跨域 同源策略 概念&#xff1a;协议&#xff0c;域名&#xff0c…...

数据结构(8.2_2)—希尔排序

希尔排序的定义&#xff1a; 第一趟&#xff1a;先将在排序表中根据增量di分别将数组元素分别插入各个子表 &#xff0c;在进行排序 代码实现&#xff1a; 算法性能分析 稳定性&#xff1a;不稳定&#xff01; 适用性&#xff1a;仅适用于顺序表&#xff0c;不适用于链表 总…...

Netty笔记

本笔记是看了黑马的Netty进行总结的。想要更详细的可以去看视频 学习netty之前要先打好NIO的基础&#xff0c;可以先去看我的另一篇文章 一、概述 不想看的可以直接跳过 Netty 的地位 Netty 在 Java 网络应用框架中的地位就好比&#xff1a;Spring 框架在 JavaEE 开发中的地位…...

管道燃气监管系统

一、建设目标 建立一个高效、智能、安全的管道燃气监管系统&#xff0c;实现对管道燃气的实时监测、风险预警、事故应急处理和数据分析&#xff0c;确保燃气供应的安全稳定&#xff0c;提高燃气管理的效率和水平。 二、系统架构 感知层 安装压力传感器、流量传感器、温度传感…...

Python语法结构(三)(Python Syntax Structure III)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...

08_Linux文件查找技巧:locate、find 和 grep 命令详解

系列文章导航&#xff1a;01_Linux基础操作CentOS7学习笔记-CSDN博客 文章目录 1. locate命令2. grep命令3. find命令 在Linux系统中&#xff0c;文件查找是一项常见的任务。本文将详细介绍三种强大的文件查找命令&#xff1a;locate、find 和 grep&#xff0c;以及它们的使用…...

JAVA 实验六

一&#xff1a; &#xff08;1&#xff09; 运行以上尟序并尣以上尟序尜尢一行尥码添加注解&#xff0c;将尟序保存尣e601.java &#xff08;2&#xff09; 以上尟序尣类变量是哪一个变量&#xff0c;类尠尞是哪一个尠尞&#xff0c;请找出类变量和类尠尞被使用尜语…...

电脑查不到IP地址是什么原因?怎么解决

在日常使用电脑的过程中&#xff0c;有时会遇到无法查询到电脑IP地址的情况&#xff0c;这可能会影响到网络的正常使用。本文将探讨电脑查不到IP地址的可能原因&#xff0c;并提供相应的解决方案。 一、原因分析 ‌网络连接问题‌&#xff1a;首先&#xff0c;网络连接不稳定或…...

Axure重要元件三——中继器修改数据

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;中继器修改数据 主要内容&#xff1a;显示编辑内容、表格赋值、修改数据 应用场景&#xff1a;更新行、表单数据行修改 案例展示&#xff1a; 正文…...

应用层——电子邮件、MIME、简单网络管理协议SNMP

电子邮件 电子邮件系统采用三个主要构件组成&#xff1a;用户代理、邮件服务器、电子邮件所需的协议 我们可以简单的认为邮件服务器中有很多邮箱&#xff0c;还有用来缓存再转发邮件的缓存&#xff0c;发送方使用用户代理通过邮件发送协议。例如SMTP将邮件发送给发送方。 邮件服…...

我与C语言二周目邂逅vlog——8.编译和链接

C语言中的编译和链接过程详细总结 1. 概述 C 语言是一种经典的系统级编程语言&#xff0c;其开发过程包括多个阶段&#xff0c;其中最关键的就是编译和链接过程。编译和链接的理解对于掌握 C 语言程序的构建至关重要。在本篇文章中&#xff0c;我们将深入讲解 C 语言的编译和…...

Views Page 视图页面

下图中显示的 Views 页面允许自定义网格级别及其相应的 View。 Views &#xff08;视图&#xff09; 页面包含两个主要部分&#xff1a; 关卡设计师;请注意&#xff0c;其他设计器页面为在关卡设计器中选择的 View 提供设置;Properties &#xff08;属性&#xff09; 窗口&…...

Win10 IDEA远程连接HBase

Win10 IDEA远程连接HBase Win10 IDEA连接虚拟机中的Hadoop&#xff08;HDFS&#xff09; 关闭Hadoop和Hbase 如果已经关闭不需要走这一步 cd /usr/local/hbase bin/stop-hbase.sh cd /usr/local/hadoop ./sbin/stop-dfs.sh获取虚拟机的ip 虚拟机终端输入 ip a关闭虚拟机…...

1.centos 镜像

centos 它有官网的下载地址&#xff1a;https://vault.centos.org/ 选择想要的版本&#xff0c;我选择 centos7.8 进入到镜像目录 isos 选择 x86_64 选择想要的版本&#xff0c;我选择 CentOS-7-x86_64-DVD-2003.iso 安装就正常安装就行。我选择虚拟机安装。这个参考&…...

electron 操作 cookie

前言&#xff1a;在 Electron 中操作 Cookie 可以使用electron模块提供的session对象来实现。 一、获取 Cookie 通过defaultSession获取默认会话对象&#xff0c;然后调用cookies.get方法并传入要获取 Cookie 的 URL 地址&#xff0c;以获取该 URL 对应的 Cookie。 const el…...

黑马软件测试第一篇_Linux

Linux 操作系统 说明: 所有硬件设备组装完成后的第⼀一层软件, 能够使⽤用户使⽤用硬件设备的软件 即为操作系统 常见分类 桌⾯面操作系统: Windows/macOS/Linux移动端操作系统: Android(安卓)/iOS(苹果)服务器器操作系统: Linux/Windows Server嵌⼊入式操作系统: Android(底…...

npm run dev 启动前端项目的原理

在一个使用 Vite 构建工具的 Vue 项目中&#xff0c;当你运行 npm run dev 时&#xff0c;实际执行的命令是 vite。为了理解这一过程&#xff0c;我们需要了解几个关键点&#xff1a; package.json 文件中的 scripts 字段: "scripts": {"dev": "vite&…...

【2024年SCI一区新算法:黑翅鸢优化算法 】分布式电网故障定位

1 场景介绍 使用10节点网络 2 故障设置 分为单重故障和两重故障 %% 2 故障设置 %% 1&#xff09;单重故障 I[1,-1,0,0,-1,-1,0,0,-1,-1]; % 区段1故障 节点状态实际编码&#xff08;是否流过故障电流&#xff09; % I[1,1,0,0,-1,-1,0,0,-1,-1]; % 区段2故障 % I[…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...