1- import { FormEventHandler } from 'react' ;
1+ import { FormEventHandler , useState } from 'react' ;
22
33import useCommentData from '@/hooks/useCommentData' ;
44import { useLoginContext } from '@/hooks/useLoginContext' ;
@@ -29,6 +29,9 @@ function CommentSubmit(props: CommentSubmitProps) {
2929 const { login, userInfo, isLogin } = useLoginContext ( ) ;
3030 const { t, belongId, className, onSuccess, onFail } = props ;
3131
32+ // 添加提交状态管理
33+ const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
34+
3235 const handleInput : FormEventHandler < HTMLTextAreaElement > = ( e ) => {
3336 const { value } = e . currentTarget ;
3437
@@ -50,6 +53,7 @@ function CommentSubmit(props: CommentSubmitProps) {
5053 const handleChangeRating = ( rating : number ) => {
5154 setCommentData ( { ...commentData , score : rating } ) ;
5255 } ;
56+
5357 const getErrMessage = ( commentData : {
5458 comment : string ;
5559 isUsed : boolean ;
@@ -70,38 +74,50 @@ function CommentSubmit(props: CommentSubmitProps) {
7074 return '' ;
7175 } ;
7276
73- const handleSubmit = ( ) => {
77+ const handleSubmit = async ( ) => {
78+ // 防止重复提交
79+ if ( isSubmitting ) {
80+ return ;
81+ }
82+
7483 if ( getErrMessage ( commentData ) ) {
7584 return Message . error ( getErrMessage ( commentData ) ) ;
7685 }
7786 if ( ! isLogin ) {
7887 return login ( ) ;
7988 }
80- let request ;
81-
82- if ( props . replyUser ) {
83- request = submitReplyComment ( props . replyUser . cid , {
84- comment : commentData . comment ,
85- reply_uid : props . replyUser . user . uid ,
86- } ) ;
87- } else {
88- request = submitComment ( belongId , commentData ) ;
89- }
9089
91- request
92- . then ( ( data ) => {
93- setCommentData ( DEFAULT_INITITAL_COMMENT_DATA ) ;
90+ // 设置提交状态
91+ setIsSubmitting ( true ) ;
9492
95- if ( data . success ) {
96- onSuccess && onSuccess ( data ) ;
97- Message . success ( t ( 'comment.submit.success' ) ) ;
98- } else {
99- onFail && onFail ( data ) ;
100- }
101- } )
102- . catch ( ( err ) => {
103- Message . error ( err . message || t ( 'comment.submit.fail' ) ) ;
104- } ) ;
93+ try {
94+ let request ;
95+
96+ if ( props . replyUser ) {
97+ request = submitReplyComment ( props . replyUser . cid , {
98+ comment : commentData . comment ,
99+ reply_uid : props . replyUser . user . uid ,
100+ } ) ;
101+ } else {
102+ request = submitComment ( belongId , commentData ) ;
103+ }
104+
105+ const data = await request ;
106+
107+ setCommentData ( DEFAULT_INITITAL_COMMENT_DATA ) ;
108+
109+ if ( data . success ) {
110+ onSuccess && onSuccess ( data ) ;
111+ Message . success ( t ( 'comment.submit.success' ) ) ;
112+ } else {
113+ onFail && onFail ( data ) ;
114+ }
115+ } catch ( err : any ) {
116+ Message . error ( err . message || t ( 'comment.submit.fail' ) ) ;
117+ } finally {
118+ // 重置提交状态
119+ setIsSubmitting ( false ) ;
120+ }
105121 } ;
106122
107123 const placeholder = props . replyUser
@@ -130,6 +146,7 @@ function CommentSubmit(props: CommentSubmitProps) {
130146 placeholder = { placeholder }
131147 value = { commentData . comment }
132148 onInput = { handleInput }
149+ disabled = { isSubmitting }
133150 > </ textarea >
134151 < div className = 'flex flex-wrap items-center gap-2 text-xs sm:gap-4 sm:text-sm' >
135152 { ! props . replyUser && (
@@ -142,6 +159,7 @@ function CommentSubmit(props: CommentSubmitProps) {
142159 style = { { boxShadow : 'none' } }
143160 checked = { ! commentData . isUsed }
144161 onChange = { ( ) => handleRadioChange ( false ) }
162+ disabled = { isSubmitting }
145163 />
146164 < span > { t ( 'comment.unused' ) } </ span >
147165 </ label >
@@ -153,6 +171,7 @@ function CommentSubmit(props: CommentSubmitProps) {
153171 style = { { boxShadow : 'none' } }
154172 checked = { commentData . isUsed }
155173 onChange = { ( ) => handleRadioChange ( true ) }
174+ disabled = { isSubmitting }
156175 />
157176 < span > { t ( 'comment.used' ) } </ span >
158177 </ label >
@@ -162,6 +181,7 @@ function CommentSubmit(props: CommentSubmitProps) {
162181 < Rating
163182 value = { commentData . score }
164183 onRateChange = { handleChangeRating }
184+ disabled = { isSubmitting }
165185 />
166186 </ div >
167187 </ >
@@ -171,23 +191,76 @@ function CommentSubmit(props: CommentSubmitProps) {
171191 < div className = 'flex flex-1 justify-end space-x-4' >
172192 < button
173193 onClick = { props . onCancelReply }
174- className = 'inline-flex h-8 min-h-[2rem] flex-shrink-0 cursor-pointer select-none flex-wrap items-center justify-center rounded-lg border border-gray-300 pl-3 pr-3 text-sm font-semibold text-gray-500 transition-transform focus:outline-none active:scale-90'
194+ className = 'inline-flex h-8 min-h-[2rem] flex-shrink-0 cursor-pointer select-none flex-wrap items-center justify-center rounded-lg border border-gray-300 pl-3 pr-3 text-sm font-semibold text-gray-500 transition-transform focus:outline-none active:scale-90 disabled:cursor-not-allowed disabled:opacity-50'
195+ disabled = { isSubmitting }
175196 >
176197 { t ( 'comment.cancel' ) }
177198 </ button >
178199 < button
179- className = 'inline-flex h-8 min-h-[2rem] flex-shrink-0 cursor-pointer select-none flex-wrap items-center justify-center rounded-lg bg-gray-700 pl-3 pr-3 text-sm font-semibold text-white transition-transform focus:outline-none active:scale-90'
200+ className = 'inline-flex h-8 min-h-[2rem] flex-shrink-0 cursor-pointer select-none flex-wrap items-center justify-center rounded-lg bg-gray-700 pl-3 pr-3 text-sm font-semibold text-white transition-transform focus:outline-none active:scale-90 disabled:cursor-not-allowed disabled:opacity-50 '
180201 onClick = { handleSubmit }
202+ disabled = { isSubmitting }
181203 >
182- { t ( 'comment.reply' ) }
204+ { isSubmitting ? (
205+ < >
206+ < svg
207+ className = 'mr-2 h-4 w-4 animate-spin'
208+ viewBox = '0 0 24 24'
209+ >
210+ < circle
211+ className = 'opacity-25'
212+ cx = '12'
213+ cy = '12'
214+ r = '10'
215+ stroke = 'currentColor'
216+ strokeWidth = '4'
217+ fill = 'none'
218+ > </ circle >
219+ < path
220+ className = 'opacity-75'
221+ fill = 'currentColor'
222+ d = 'M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'
223+ > </ path >
224+ </ svg >
225+ { t ( 'comment.submitting' ) || '提交中...' }
226+ </ >
227+ ) : (
228+ t ( 'comment.reply' )
229+ ) }
183230 </ button >
184231 </ div >
185232 ) : (
186233 < button
187- className = 'ml-auto inline-flex h-8 min-h-[2rem] flex-shrink-0 cursor-pointer select-none flex-wrap items-center justify-center rounded-lg bg-gray-700 pl-3 pr-3 text-sm font-semibold text-white transition-transform focus:outline-none active:scale-90'
234+ className = 'ml-auto inline-flex h-8 min-h-[2rem] flex-shrink-0 cursor-pointer select-none flex-wrap items-center justify-center rounded-lg bg-gray-700 pl-3 pr-3 text-sm font-semibold text-white transition-transform focus:outline-none active:scale-90 disabled:cursor-not-allowed disabled:opacity-50 '
188235 onClick = { handleSubmit }
236+ disabled = { isSubmitting }
189237 >
190- { t ( 'comment.submit.save' ) }
238+ { isSubmitting ? (
239+ < >
240+ < svg
241+ className = 'mr-2 h-4 w-4 animate-spin'
242+ viewBox = '0 0 24 24'
243+ >
244+ < circle
245+ className = 'opacity-25'
246+ cx = '12'
247+ cy = '12'
248+ r = '10'
249+ stroke = 'currentColor'
250+ strokeWidth = '4'
251+ fill = 'none'
252+ > </ circle >
253+ < path
254+ className = 'opacity-75'
255+ fill = 'currentColor'
256+ d = 'M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'
257+ > </ path >
258+ </ svg >
259+ { t ( 'comment.submitting' ) }
260+ </ >
261+ ) : (
262+ t ( 'comment.submit.save' )
263+ ) }
191264 </ button >
192265 ) }
193266 </ >
0 commit comments