1+ "use client" ;
2+
3+ import {
4+ Button ,
5+ Card ,
6+ CardBody ,
7+ Divider ,
8+ Input ,
9+ Modal ,
10+ ModalBody ,
11+ ModalContent ,
12+ ModalFooter ,
13+ ModalHeader ,
14+ Switch
15+ } from "@heroui/react" ;
16+ import React , { forwardRef , useImperativeHandle } from "react" ;
17+
18+ import { z } from "zod" ;
19+ import { useForm } from "react-hook-form" ;
20+ import { zodResolver } from "@hookform/resolvers/zod" ;
21+
22+ // 定义表单验证 schema
23+ const securitySettingsSchema = z . object ( {
24+ twoFactorEnabled : z . boolean ( ) ,
25+ } ) ;
26+
27+ type SecuritySettingsForm = z . infer < typeof securitySettingsSchema > ;
28+
29+ // 定义组件 ref 类型
30+ export type SecuritySettingsRef = {
31+ submitForm : ( ) => Promise < void > ;
32+ resetForm : ( ) => void ;
33+ } ;
34+
35+ const SecuritySettings = forwardRef < SecuritySettingsRef > ( ( props , ref ) => {
36+ const [ isChangePasswordOpen , setIsChangePasswordOpen ] = React . useState ( false ) ;
37+ const [ currentPassword , setCurrentPassword ] = React . useState ( "" ) ;
38+ const [ newPassword , setNewPassword ] = React . useState ( "" ) ;
39+ const [ confirmPassword , setConfirmPassword ] = React . useState ( "" ) ;
40+
41+ // 初始化表单
42+ const {
43+ register,
44+ handleSubmit,
45+ reset,
46+ formState : { errors } ,
47+ } = useForm < SecuritySettingsForm > ( {
48+ resolver : zodResolver ( securitySettingsSchema ) ,
49+ defaultValues : {
50+ twoFactorEnabled : true ,
51+ } ,
52+ } ) ;
53+
54+ const handleChangePassword = ( ) => {
55+ // TODO: 实现密码修改逻辑
56+ console . log ( "修改密码" , { currentPassword, newPassword, confirmPassword } ) ;
57+ setIsChangePasswordOpen ( false ) ;
58+ // 重置表单
59+ setCurrentPassword ( "" ) ;
60+ setNewPassword ( "" ) ;
61+ setConfirmPassword ( "" ) ;
62+ } ;
63+
64+ // 处理表单提交
65+ const onSubmit = async ( data : SecuritySettingsForm ) => {
66+ try {
67+ // TODO: 调用后端 API 保存设置
68+ console . log ( "保存设置:" , data ) ;
69+ } catch ( error ) {
70+ console . error ( "保存设置失败:" , error ) ;
71+ throw error ;
72+ }
73+ } ;
74+
75+ // 暴露方法给父组件
76+ useImperativeHandle ( ref , ( ) => ( {
77+ submitForm : ( ) => handleSubmit ( onSubmit ) ( ) ,
78+ resetForm : ( ) => reset ( ) ,
79+ } ) ) ;
80+
81+ return (
82+ < form >
83+ < Card className = "mt-5 p-2" >
84+ < CardBody className = "gap-8" >
85+ < div className = "space-y-6" >
86+ < div className = "flex items-center justify-between" >
87+ < div className = "space-y-1" >
88+ < h3 className = "text-base font-medium" > 修改密码</ h3 >
89+ < p className = "text-sm text-default-500" > 定期更新密码以提高账户安全性</ p >
90+ </ div >
91+ < Button
92+ color = "primary"
93+ variant = "flat"
94+ onPress = { ( ) => setIsChangePasswordOpen ( true ) }
95+ >
96+ 修改密码
97+ </ Button >
98+ </ div >
99+ < Divider />
100+ < div className = "flex items-center justify-between" >
101+ < div className = "space-y-1" >
102+ < h3 className = "text-base font-medium" > 双因素认证</ h3 >
103+ < p className = "text-sm text-default-500" > 使用验证器应用进行双重身份验证</ p >
104+ </ div >
105+ < Switch { ...register ( "twoFactorEnabled" ) } />
106+ </ div >
107+ < Divider />
108+ < div className = "flex items-center justify-between" >
109+ < div className = "space-y-1" >
110+ < h3 className = "text-base font-medium" > 登录设备管理</ h3 >
111+ < p className = "text-sm text-default-500" > 查看并管理已登录的设备</ p >
112+ </ div >
113+ < Button color = "primary" variant = "flat" >
114+ 查看设备
115+ </ Button >
116+ </ div >
117+ < Divider />
118+ < div className = "flex items-center justify-between" >
119+ < div className = "space-y-1" >
120+ < h3 className = "text-base font-medium" > 登录历史记录</ h3 >
121+ < p className = "text-sm text-default-500" > 查看近期的登录活动</ p >
122+ </ div >
123+ < Button color = "primary" variant = "flat" >
124+ 查看记录
125+ </ Button >
126+ </ div >
127+ </ div >
128+ </ CardBody >
129+ </ Card >
130+
131+ < Modal
132+ isOpen = { isChangePasswordOpen }
133+ onOpenChange = { setIsChangePasswordOpen }
134+ placement = "center"
135+ backdrop = "blur"
136+ >
137+ < ModalContent >
138+ { ( onClose ) => (
139+ < >
140+ < ModalHeader className = "flex flex-col gap-1" >
141+ < h3 className = "text-lg font-medium" > 修改密码</ h3 >
142+ < p className = "text-sm text-default-500" > 请输入您的当前密码和新密码</ p >
143+ </ ModalHeader >
144+ < ModalBody >
145+ < div className = "space-y-4" >
146+ < div className = "space-y-2" >
147+ < label className = "text-sm text-default-700" > 当前密码</ label >
148+ < Input
149+ type = "password"
150+ variant = "bordered"
151+ placeholder = "输入当前密码"
152+ value = { currentPassword }
153+ onValueChange = { setCurrentPassword }
154+ />
155+ </ div >
156+ < div className = "space-y-2" >
157+ < label className = "text-sm text-default-700" > 新密码</ label >
158+ < Input
159+ type = "password"
160+ variant = "bordered"
161+ placeholder = "输入新密码"
162+ value = { newPassword }
163+ onValueChange = { setNewPassword }
164+ />
165+ </ div >
166+ < div className = "space-y-2" >
167+ < label className = "text-sm text-default-700" > 确认新密码</ label >
168+ < Input
169+ type = "password"
170+ variant = "bordered"
171+ placeholder = "再次输入新密码"
172+ value = { confirmPassword }
173+ onValueChange = { setConfirmPassword }
174+ isInvalid = { confirmPassword !== "" && newPassword !== confirmPassword }
175+ errorMessage = {
176+ confirmPassword !== "" &&
177+ newPassword !== confirmPassword &&
178+ "两次输入的密码不一致"
179+ }
180+ />
181+ </ div >
182+ </ div >
183+ </ ModalBody >
184+ < ModalFooter >
185+ < Button
186+ color = "danger"
187+ variant = "light"
188+ onPress = { onClose }
189+ >
190+ 取消
191+ </ Button >
192+ < Button
193+ color = "primary"
194+ onPress = { handleChangePassword }
195+ isDisabled = {
196+ ! currentPassword ||
197+ ! newPassword ||
198+ ! confirmPassword ||
199+ newPassword !== confirmPassword
200+ }
201+ >
202+ 确认修改
203+ </ Button >
204+ </ ModalFooter >
205+ </ >
206+ ) }
207+ </ ModalContent >
208+ </ Modal >
209+ </ form >
210+ ) ;
211+ } ) ;
212+
213+ SecuritySettings . displayName = "SecuritySettings" ;
214+
215+ export default SecuritySettings ;
0 commit comments