From 0ebda96d627393a71add2da17996646e01daf828 Mon Sep 17 00:00:00 2001 From: tone <3341154833@qq.com> Date: Sun, 18 May 2025 23:18:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BF=AE=E6=94=B9=E5=AF=86?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E4=BD=86=E5=BC=95=E5=85=A5=E4=BA=86=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=AF=86=E7=A0=81=E5=90=8E=E6=97=A0=E6=B3=95=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E7=AA=97=E5=8F=A3=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/user/dto/update-user-password.dto.ts | 10 ++ tone-page-server/src/user/user.controller.ts | 19 +- tone-page-web/components/nav-user.tsx | 162 +++++++++--------- .../components/nav-user/SetPassword.tsx | 76 ++++++++ tone-page-web/lib/api/user/index.ts | 3 +- tone-page-web/lib/api/user/updatePassword.ts | 10 ++ 6 files changed, 197 insertions(+), 83 deletions(-) create mode 100644 tone-page-server/src/user/dto/update-user-password.dto.ts create mode 100644 tone-page-web/components/nav-user/SetPassword.tsx create mode 100644 tone-page-web/lib/api/user/updatePassword.ts diff --git a/tone-page-server/src/user/dto/update-user-password.dto.ts b/tone-page-server/src/user/dto/update-user-password.dto.ts new file mode 100644 index 0000000..e437759 --- /dev/null +++ b/tone-page-server/src/user/dto/update-user-password.dto.ts @@ -0,0 +1,10 @@ +import { IsString, Length, Matches } from "class-validator"; + +export class UpdateUserPasswordDto { + @IsString({ message: '密码不得为空' }) + @Length(6, 32, { message: '密码长度只能为6~32' }) + @Matches(/^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d!@#$%^&*()_+\-=\[\]{};:'",.<>/?]{6,32}$/, + { message: '密码必须包含字母和数字,且长度在6~32之间' } + ) + password: string; +} \ No newline at end of file diff --git a/tone-page-server/src/user/user.controller.ts b/tone-page-server/src/user/user.controller.ts index 4a14c4a..78603d6 100644 --- a/tone-page-server/src/user/user.controller.ts +++ b/tone-page-server/src/user/user.controller.ts @@ -1,21 +1,30 @@ -import { Controller, Get, Injectable, Request, UnauthorizedException, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Put, Request, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; import { AuthGuard } from '@nestjs/passport'; +import { UpdateUserPasswordDto } from './dto/update-user-password.dto'; +import { AuthService } from 'src/auth/auth.service'; @Controller('user') export class UserController { constructor( - private readonly userService: UserService + private readonly userService: UserService, + private readonly authService: AuthService, ) { } @UseGuards(AuthGuard('jwt')) @Get('me') async getMe(@Request() req) { const { user } = req; - if (!user || !user.userId) { - throw new UnauthorizedException('Unauthorized'); - } return this.userService.findOne({ userId: user.userId }); } + + @UseGuards(AuthGuard('jwt')) + @Put('password') + async update( + @Request() req, + @Body() dto: UpdateUserPasswordDto, + ) { + return this.userService.setPassword(req.user.userId, dto.password); + } } diff --git a/tone-page-web/components/nav-user.tsx b/tone-page-web/components/nav-user.tsx index d7f041c..8ce6421 100644 --- a/tone-page-web/components/nav-user.tsx +++ b/tone-page-web/components/nav-user.tsx @@ -32,6 +32,8 @@ import { Skeleton } from "./ui/skeleton" import { toast } from "sonner" import { useRouter } from "next/navigation" import { ApiError } from "next/dist/server/api-utils" +import SetPassword from "./nav-user/SetPassword" +import { useState } from "react" export function NavUser({ }: {}) { const { isMobile } = useSidebar(); @@ -68,86 +70,92 @@ export function NavUser({ }: {}) { } } + const [passwordOpen, setPasswordOpen] = useState(false); + return ( - - - - - + + + + + + { + user && <> + + + U + +
+ {user.nickname} + {user.username} +
+ + } + { + isLoading &&
+ +
+ + +
+
+ } + +
+
+ - { - user && <> - - - U - -
- {user.nickname} - {user.username} + + { + user && +
+ + + U + +
+ {user.nickname} + {user.username} +
- - } - { - isLoading &&
- -
- - + } + { + isLoading &&
+ +
+ + +
-
- } - - - - - - { - user && -
- - - U - -
- {user.nickname} - {user.username} -
-
- } - { - isLoading &&
- -
- - -
-
- } -
- - - - 账户信息 - - - - 修改密码 - - - - - 登出 - -
- - - + } + + + + + 账户信息 + + setPasswordOpen(true)}> + + 修改密码 + + + + + 登出 + + + + + + + + ) } diff --git a/tone-page-web/components/nav-user/SetPassword.tsx b/tone-page-web/components/nav-user/SetPassword.tsx new file mode 100644 index 0000000..cf0740b --- /dev/null +++ b/tone-page-web/components/nav-user/SetPassword.tsx @@ -0,0 +1,76 @@ +'use client'; + +import { SeparatorProps } from "@radix-ui/react-separator"; +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { FC } from "react"; +import { DialogProps } from "@radix-ui/react-dialog"; +import { toast } from "sonner"; +import { UserApi } from "@/lib/api"; +import { ApiError } from "next/dist/server/api-utils"; + +export default function SetPassword({ onOpenChange, ...props }: React.ComponentProps>) { + async function handleSetPassword(password: string) { + if (! /^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d!@#$%^&*()_+\-=\[\]{};:'",.<>/?]{6,32}$/.test(password)) { + toast.error('新密码不符合规范,请重新输入'); + return; + } + + try { + await UserApi.updatePassword(password); + toast.success('新密码设置成功'); + onOpenChange?.(false); + } catch (error) { + toast.error((error as ApiError).message || '新密码设置失败'); + } + } + + return ( + + + + 修改密码 + + 新密码长度在6-32位之间,且至少包含一个字母和一个数字,可以包含特殊字符 + + + +
{ + e.preventDefault(); + const formData = new FormData(e.currentTarget); + const password = formData.get('password') as string; + + handleSetPassword(password); + }}> +
+
+ + +
+
+ + + + +
+
+
+ ) +} \ No newline at end of file diff --git a/tone-page-web/lib/api/user/index.ts b/tone-page-web/lib/api/user/index.ts index 57dfe1e..ccc906e 100644 --- a/tone-page-web/lib/api/user/index.ts +++ b/tone-page-web/lib/api/user/index.ts @@ -1 +1,2 @@ -export * from './me'; \ No newline at end of file +export * from './me'; +export * from './updatePassword'; \ No newline at end of file diff --git a/tone-page-web/lib/api/user/updatePassword.ts b/tone-page-web/lib/api/user/updatePassword.ts new file mode 100644 index 0000000..04ba91d --- /dev/null +++ b/tone-page-web/lib/api/user/updatePassword.ts @@ -0,0 +1,10 @@ +import fetcher from "../fetcher"; + +export async function updatePassword(password: string) { + return fetcher(`/api/user/password`, { + method: 'PUT', + body: JSON.stringify({ + password: password, + }), + }) +} \ No newline at end of file