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

【Next.js 项目实战系列】02-创建 Issue

原文链接

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

上一篇【Next.js 项目实战系列】01-创建项目

创建 Issue

配置 MySQL 与 Prisma​

在数据库中可以找到相关内容,这里不再赘述

添加 model​

本节代码链接

# 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()
}enum Status {OPENIN_PROGRESSCLOSED
}

使用以下指令同步到数据库

npx prisma format
npx prisma migrate dev

编写 API​

本节代码链接

这里使用 zod 来验证表单,具体内容可参考使用 zod 验证表单

# /app/api/issues/route.tsimport { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import prisma from "@/prisma/client";const createIssueSchema = z.object({title: z.string().min(1).max(255),description: z.string().min(1),
});export async function POST(request: NextRequest) {const body = await request.json();const validation = createIssueSchema.safeParse(body);if (!validation.success)return NextResponse.json(validation.error.errors, { status: 400 });const newIssue = await prisma.issue.create({data: { title: body.title, description: body.description },});return NextResponse.json(newIssue, { status: 201 });
}

Radix-UI​

本节代码链接

radix-ui 也是一个类 DaisyUI 的组件库,使用如下指令安装

npm install @radix-ui/themes

安装好后,进行如下初始配置,将主 layout 中的所有内容用 <Theme > 标签包起来

# /app/layout.tsximport type { Metadata } from "next";
+ import "@radix-ui/themes/styles.css";import { Inter } from "next/font/google";
+ import { Theme } from "@radix-ui/themes";import "./globals.css";import NavBar from "./NavBar";const inter = Inter({ subsets: ["latin"] });export const metadata: Metadata = {title: "Create Next App",description: "Generated by create next app",};export default function RootLayout({children,}: Readonly<{children: React.ReactNode;}>) {return (<html lang="en"><body className={inter.className}>
+         <Theme><NavBar /><main>{children}</main>
+         </Theme></body></html>);}

创建新 Issue 页面​

本节代码链接

# /app/issues/new/page.tsx"use client";
import { Button, TextArea, TextField } from "@radix-ui/themes";const NewIssuePage = () => {return (<div className="max-w-xl space-y-3"><TextField.Root><TextField.Input placeholder="Title" /></TextField.Root><TextArea placeholder="Description" /><Button>Submit New</Button></div>);
};
export default NewIssuePage;

显示效果如下 

New Issue Page

Radix-UI 定义 UI 样式​

本节代码链接

在 layout.tsx 中添加 <Themepanel >

# /app/layout.tsx+ import { Theme, ThemePanel } from "@radix-ui/themes";...return (<html lang="en"><body className={inter.className}><Theme><NavBar /><main className="p-5">{children}</main>
+           <ThemePanel /></Theme></body></html>);...

效果如下

Theme Panel

调整好自己想要的样式之后点击 Copy Theme,将 copy 到的 <Theme > 标签替换掉原来的即可

  #  /app/layout.tsx...return (<html lang="en"><body className={inter.className}>{/*添加到这里即可*/}<Theme appearance="light" accentColor="violet"><NavBar /><main className="p-5">{children}</main></Theme></body></html>);...

设置字体​

在 Radix-UI 中设置字体需要以下步骤,可以参考 radix-ui-font

首先在 layout.tsx 中修改

# /app/layout.tsximport { Theme } from "@radix-ui/themes";import "@radix-ui/themes/styles.css";import type { Metadata } from "next";import { Inter } from "next/font/google";import NavBar from "./NavBar";import "./globals.css";
- const inter = Inter({ subsets: ["latin"] });
+ const inter = Inter({
+   subsets: ['latin'],
+   variable: '--font-inter',
+ });export const metadata: Metadata = {title: "Create Next App",description: "Generated by create next app",};export default function RootLayout({children,}: Readonly<{children: React.ReactNode;}>) {return (<html lang="en">
-       <body className={inter.className}>
+       <body className={inter.variable}><Theme appearance="light" accentColor="violet"><NavBar /><main className="p-5">{children}</main></Theme></body></html>);}

然后添加 /app/theme-config.css 并添加以下内容

/app/theme-config.css

.radix-themes {--default-font-family: var(--font-inter);
}

最后在 layout.tsx 中 import 进来即可

···
import "./theme-config.css";
···

MarkDown Editor​

本节代码链接

react-simlemde-editor 是一款集成式 MarkDown 编辑器,使用如下命令安装

npm install --save react-simplemde-editor easymde

效果如下:

Simple MarkDown Editor

提交表单​

本节代码链接

我们使用 react-hook-form 和 axios 进行表单提交

npm i react-hook-form
npm i axios
# /app/issues/new/page.tsx"use client";import { Button, TextField } from "@radix-ui/themes";import { useRouter } from "next/navigation";// import
+ import axios from "axios";
+ import "easymde/dist/easymde.min.css";
+ import { Controller, useForm } from "react-hook-form";
+ import SimpleMDE from "react-simplemde-editor";// 使用 interface 表明 form 中有哪些内容
+ interface IssueForm {
+   title: string;
+   description: string;
+ }const NewIssuePage = () => {// 使用 React Hook Form
+   const { register, control, handleSubmit } = useForm<IssueForm>();// 使用 router 进行页面跳转
+   const router = useRouter();return ({/* 将最外层 div 换为 form */}
+     <form className="max-w-xl space-y-3"
+       onSubmit={handleSubmit(async (data) => {{/* 使用 axios 进行 post */}
+         await axios.post("/api/issues", data);
+         router.push("/issues");
+       })}><TextField.Root>{/* 将该组件注册为 form 中的 title 字段 */}
+         <TextField.Input placeholder="Title" {...register("title")} /></TextField.Root>{/* 由于 simpleMDE 不能直接像上面的 Input 一样传入参数,我们这里使用 React Hook Form 中的 Controller */}
-       <SimpleMDE placeholder="Description" />
+       <Controller
+         name="description"
+         control={control}
+         render={({ field }) => (
+           <SimpleMDE placeholder="Description" {...field} />
+         )}
+       /><Button>Submit New</Button>
+     </form>);};export default NewIssuePage;

完整代码(非 git diff 版)

# /app/issues/new/page.tsx"use client";
import { Button, TextField } from "@radix-ui/themes";
import axios from "axios";
import "easymde/dist/easymde.min.css";
import { useRouter } from "next/navigation";
import { Controller, useForm } from "react-hook-form";
import SimpleMDE from "react-simplemde-editor";interface IssueForm {title: string;description: string;
}const NewIssuePage = () => {const { register, control, handleSubmit } = useForm<IssueForm>();const router = useRouter();return (<formclassName="max-w-xl space-y-3"onSubmit={handleSubmit(async (data) => {await axios.post("/api/issues", data);router.push("/issues");})}><TextField.Root><TextField.Input placeholder="Title" {...register("title")} /></TextField.Root><Controllername="description"control={control}render={({ field }) => (<SimpleMDE placeholder="Description" {...field} />)}/><Button>Submit New</Button></form>);
};
export default NewIssuePage;

效果如下:

submit form

Handle Error​

本节代码链接

表单验证​

之前说到,我们使用 zod 进行表单验证,可以在使用 zod 时,自定义报错内容

# /app/api/issues/new/route.tsx...const createIssueSchema = z.object({// 在定义时,可以加第二个参数,表示如果未满足该项时的报错
+   title: z.string().min(1, "Title is required!").max(255),
+   description: z.string().min(1, "Description is required!"),});export async function POST(request: NextRequest) {...if (!validation.success)// 改为调用 validation.error.format()
-     return NextResponse.json(validation.error.errors, { status: 400 });
+     return NextResponse.json(validation.error.format(), { status: 400 });...}

报错显示​

接下来实现一个这样的 Error Callout

Error Callout

在 /app/issues/new/page.tsx 中修改。把 axios 的相关内容放到一个 try-catch block 里

# /app/issues/new/page.tsx"use client";...const NewIssuePage = () => {...// 添加 useState 变量
+   const [error, setError] = useState("");return (...{/*若报错则显示一个 CallOut*/}
+       {error && (
+         <Callout.Root color="red" className="mb-5">
+           <Callout.Text>{error}</Callout.Text>
+         </Callout.Root>
+       )}<formclassName="space-y-3"onSubmit={handleSubmit(async (data) => {// 报错时设置 error
+           try {
+             await axios.post("/api/issues", data);
+             router.push("/issues");
+           } catch (error) {
+             setError("An unexpected Error occured!");
+           }})}>...};export default NewIssuePage;

用户端验证​

本节代码链接

Zod schema​

我们在用户端验证时,也需要用到刚刚 zod 中编辑的 schema,为此我们应该将其移动到一个单独的文件中。在 VS Code 中 可以方便的进行重构,将 createIssueSchema 移动到一个新文件中,并自动更新引用

首先右键想要重构的变量,点击 重构

Refactor 1

然后选择 move to a new file

Refactor 2

使用 Zod Schema 推断 interface​

将刚刚移出的 schema 移动到 /app 目录下,重命名为 validationSchema.ts

之前在 new page 中,我们定义了一个 interface,用于定义表单,但其实与我们在 zod 中定义的内容是重复的,如果我们之后还需要增删内容,需要在两边修改,较为麻烦。我们可以直接使用刚刚的 zod schema 来定义 interface ,如下所示

# /app/issues/new/page.tsx+  import { createIssueSchema } from "@/app/validationSchema";
+  import { z } from "zod";
- interface IssueForm {
-   title: string;
-   description: string;
- }
+  type IssueForm = z.infer<typeof createIssueSchema>;

使用 hookform 集成 zod 验证表单​

安装 hookform/resolvers,用于将 React Hook Form 插件使用表单验证插件(比如 zod)

npm i @hookform/resolvers
# /app/issues/new/page.tsx"use client";...// import
+ import { Button, Callout, Text, TextField } from "@radix-ui/themes";
+ import { zodResolver } from "@hookform/resolvers/zod";type IssueForm = z.infer<typeof createIssueSchema>;const NewIssuePage = () => {const {register,control,handleSubmit,// errors 则为验证结果
+     formState: { errors },} = useForm<IssueForm>({// 将 zodResoler 传入,以验证表单
+     resolver: zodResolver(createIssueSchema),});...return (<div className="max-w-xl">...<TextField.Root><TextField.Input placeholder="Title" {...register("title")} /></TextField.Root>{/* 根据验证结果来显示提示,此处为 title 字段的信息 */}
+       {errors.title && (
+         <Text color="red" as="p">
+           {errors.title.message}
+         </Text>
+       )}<Controllername="description"control={control}render={({ field }) => (<SimpleMDE placeholder="Description" {...field} />)}/>{/* 根据验证结果来显示提示,此处为 description 字段的信息 */}
+       {errors.description && (
+         <Text color="red" as="p">
+           {errors.description.message}
+         </Text>
+       )}...</div>);};export default NewIssuePage;

最终效果如下:

Client Side Validation

将 ErrorMessage 封装​

# /app/components/ErrorMessage.tsximport { Text } from "@radix-ui/themes";
import { PropsWithChildren } from "react";const ErrorMessage = ({ children }: PropsWithChildren) => {if (!children) return null;return (<Text color="red" as="p">{children}</Text>);
};
export default ErrorMessage;

 然后我们可以在 new Page 中直接调用

# /app/issues/new/page.tsx"use client";...// import
+ import ErrorMessage from "@/app/components/ErrorMessage";return (<div className="max-w-xl">...{/* 根据验证结果来显示提示,此处为 title 字段的信息 */}
-       {errors.title && (
-         <Text color="red" as="p">
-           {errors.title.message}
-         </Text>
-       )}
+       <ErrorMessage>{errors.title?.message}</ErrorMessage>...
-       {errors.description && (
-         <Text color="red" as="p">
-           {errors.description.message}
-         </Text>
-       )}
+        <ErrorMessage>{errors.description?.message}</ErrorMessage>...</div>);};export default NewIssuePage;

Button 优化技巧​

本节代码链接

首先我们可以添加一个 Spinner 给 Button。其次,我们可以给 Button 添加一个 disabled 属性,使得其只能被点击一次,避免多次提交表单

Spinner 代码

# /app/issues/new/page.tsx+ import Spinner from "@/app/components/Spinner";const NewIssuePage = () => {
+   const [isSubmitting, setSubmitting] = useState(false);return (<div className="max-w-xl">...<formclassName="space-y-3"onSubmit={handleSubmit(async (data) => {try {
+             setSubmitting(true);await axios.post("/api/issues", data);router.push("/issues");} catch (error) {
+             setSubmitting(false);setError("An unexpected Error occured!");}})}>...
+         <Button disabled={isSubmitting}>
+           Submit New Issue {isSubmitting && <Spinner />}
+         </Button></form></div>);};

最终版本​

本节代码链接

/app/issues/new/page.tsx"use client";
import { Button, Callout, Text, TextField } from "@radix-ui/themes";
import axios from "axios";
import "easymde/dist/easymde.min.css";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import SimpleMDE from "react-simplemde-editor";
import { zodResolver } from "@hookform/resolvers/zod";
import { createIssueSchema } from "@/app/validationSchema";
import { z } from "zod";
import ErrorMessage from "@/app/components/ErrorMessage";type IssueForm = z.infer<typeof createIssueSchema>;const NewIssuePage = () => {const {register,control,handleSubmit,formState: { errors },} = useForm<IssueForm>({resolver: zodResolver(createIssueSchema),});const router = useRouter();const [error, setError] = useState("");return (<div className="max-w-xl">{error && (<Callout.Root color="red" className="mb-5"><Callout.Text>{error}</Callout.Text></Callout.Root>)}<formclassName="space-y-3"onSubmit={handleSubmit(async (data) => {try {await axios.post("/api/issues", data);router.push("/issues");} catch (error) {setError("An unexpected Error occured!");}})}><TextField.Root><TextField.Input placeholder="Title" {...register("title")} /></TextField.Root><ErrorMessage>{errors.title?.message}</ErrorMessage><Controllername="description"control={control}render={({ field }) => (<SimpleMDE placeholder="Description" {...field} />)}/><ErrorMessage>{errors.description?.message}</ErrorMessage><Button>Submit New</Button></form></div>);
};
export default NewIssuePage;

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

下一篇讲查看 Issue

下一篇【Next.js 项目实战系列】03-查看 Issue

相关文章:

【Next.js 项目实战系列】02-创建 Issue

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】01-创建项目 创建 Issue 配置 MySQL 与 Prisma​ 在数据库中可以找到相关内容&…...

浅谈C++的future

std::future 是 C 标准库中的一个模板类&#xff0c;提供了一种机制来管理和获取异步任务的结果。它常与异步操作相关&#xff0c;允许你在不同线程中执行任务&#xff0c;并在将来&#xff08;即“未来”&#xff09;某个时刻获取这些任务的结果。std::future 通常和 std::asy…...

期货外盘行情源7个市场CTP推送式服务说明

在期货交易领域&#xff0c;及时、准确的市场行情信息是投资者做出决策的重要依据。为了满足广大期货投资者对国际期货市场信息的迫切需求&#xff0c;我们特别推出了“期货外盘行情源2千每月7个市场CTP推送式”服务。本服务旨在通过高效、稳定的技术手段&#xff0c;为投资者提…...

计算机毕业设计 | SSM 校园线上订餐系统(附源码)

1&#xff0c; 概述 1.1 项目背景 传统的外卖方式就是打电话预定&#xff0c;然而&#xff0c;在这种方式中&#xff0c;顾客往往通过餐厅散发的传单来获取餐厅的相关信息&#xff0c;通过电话来传达自己的订单信息&#xff0c;餐厅方面通过电话接受订单后&#xff0c;一般通…...

【iOS】使用一个单例通过AFNetworking来实现网络请求

【iOS】使用一个单例通过AFNetworking来实现网络请求 文章目录 【iOS】使用一个单例通过AFNetworking来实现网络请求前言OC网络请求的流程 使用单例的原因创建一个单例采用AFNetworking的网络申请 小结 前言 笔者这周主要学习了第三方库AFNetworking的使用&#xff0c;这里笔者…...

如何从模块内部运行 Pytest

在 Python 中&#xff0c;pytest 是一个强大的测试框架&#xff0c;用于编写和运行测试用例。通常我们会在命令行中运行 pytest&#xff0c;但是有时你可能希望从模块或脚本的内部运行 pytest&#xff0c;比如为了自动化测试或集成到某个工作流程中。 1、问题背景 当你从模块…...

oracle数据库---基本查询(单表查询、多表查询、子查询、分页查询、oracle内置函数、行列转换、集合运算)

思维导图 单表查询 数据准备 -- 练习的表如果存在 请先删除 -- 如果不存在直接创建 drop table t_owners;--业主表 create table t_owners (id number primary key,name varchar2(30),addressid number,housenumber varchar2(30),watermeter varchar2(30),adddate date,owner…...

web API基础

作用和分类 作用: 就是使用 JS 去操作 html 和浏览器 分类&#xff1a; DOM (文档对象模型)、 BOM &#xff08;浏览器对象模型&#xff09; 什么是DOM DOM (Document Object Model) 译为文档对象模型&#xff0c;是 HTML 和 XML 文档的编程接口。 HTML DOM 定义了访问和操作 …...

【C++】创建TCP服务端

实现了一个基本的 TCP 服务器&#xff0c;可以接受多个客户端连接&#xff0c;然后持续接收客户端发送的信息&#xff0c; 最后将接收到的信息再发送回客户端 。 源码 头文件&#xff08;TCPServerTest.h&#xff09; #include <iostream> #include <winsock2.h&g…...

每天练打字6:今日状况——常用字后五百击键3第2遍已完成,赛文速度93.56

今日跟打&#xff1a;763字 &#xff08;截至当前&#xff09; 总跟打&#xff1a;120408字 记录天数&#xff1a;2467天 &#xff08;实际没有这么多天&#xff0c;这个是注册账号的天数&#xff09; 平均每天&#xff1a;48字 本周目标完成进度&#xff1a; 练习常用单字后5…...

rk3568创建基于Ubuntu18.04交叉编译遇到的坑

尽管配置过rk3288&#xff0c;觉得rk3568也不会有啥问题&#xff0c;但还是掉坑了。 一、安装依赖库 安装完ubuntu后&#xff0c;先进行升级 sudo apt update sudo apt upgrade然后安装依赖库 $ sudo apt-get install repo git-core gitk git-gui gcc-arm-linux-gnueabihf u-…...

对“一个中心,三重防护”中安全管理中心的理解

安全管理中心 本控制项为网络安全等级保护标准的技术部分。本项主要包括系统管理、审计管理、安全管理和集中管控四个控制点&#xff0c;其中的集中管控可以说是重中之重&#xff0c;主要都是围绕它来展开的。 28448基本要求中安全管理中心 8.1.5 安全管理中心 8.1.5.1 系统…...

jmeter用csv data set config做参数化1

在jmeter中&#xff0c;csv data set config的作用非常强大&#xff0c;用它来做批量测试和参数化非常好用。 csv data set config的常用配置项如下&#xff1a; Variable Names处&#xff0c;写上源文件中的参数名&#xff0c;用于后续接口发送请求时引用 Ignore first line…...

软件分享 丨eSearch 截图软件

在日常的工作和学习中&#xff0c;截图是一项非常常见且重要的操作。无论是记录重要的信息、制作教程&#xff0c;还是与他人分享有趣的内容&#xff0c;一款好用的截图软件都能让我们的效率大大提高。今天&#xff0c;就给大家分享一款功能强大的截图软件 - eSearch。 一、强…...

pytorh学习笔记——cifar10(一)生成数据

CIFAR&#xff08;Canadian Institute For Advanced Research&#xff09;是一个用于图像识别研究的数据集。CIFAR数据集包含多个子数据集&#xff0c;最常用的是CIFAR-10和CIFAR-100。 CIFAR-10数据集包含60000张32x32彩色图像&#xff0c;分为10个类别&#xff0c;每…...

pikachu靶场SSRF-curl测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、实现ssrf攻击 四、源代码分析 五、结论 一、测试环境 1、系统环境 渗透机&#xff1a;本机(127.0.0.1) 靶 机&#xff1a;本机(127.0.0.1) 2、使用工具/软件 测试网址&#xff1a;…...

Java项目-基于Springboot的农机电招平台项目(源码+说明).zip

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…...

Systemd:简介

1号进程 Systemd是linux系统的守护进程&#xff0c;它要管理正在运行的 Linux 主机的许多方面&#xff0c;包括挂载文件系统、管理硬件、处理定时器以及启动和管理生产性主机所需的系统服务。 $ ps -u -p 1 USER PID %CPU %MEM VSZ RSS TTY STAT START TI…...

Snapshot包和Release包区别

Snapshot包和Release包区别 Snapshot包&#xff1a; 代表开发中的版本&#xff0c;可能不稳定。 经常更新&#xff0c;包含最新的功能和修复。 通常用于测试和开发阶段。 Release包&#xff1a; 代表稳定的、经过全面测试的版本。 版本号通常是固定的&#xff0c;适合生产环…...

最近网站频繁跳转到黑产网站,怀疑是51.la统计代码的问题

最近我的几个网站&#xff0c;都出现了一个问题&#xff0c;就是访问的时候会莫名其妙的跳转到黑产网站。 通过排查了网页代码&#xff0c;发现网页都有一个共同点&#xff0c;就是使用了51.la统计。为什么会怀疑是51la统计代码问题&#xff1f;因为我的网页只有统计代码外没有…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

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 为工程 名&…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...