@@ -27,26 +27,7 @@ const RandomNamePickerContent = () => {
2727
2828 // 初始化:加载URL名单和本地收藏名单
2929 useEffect ( ( ) => {
30- const list = searchParams . get ( 'list' ) ;
31- if ( list ) {
32- const decodedList = decodeURIComponent ( list ) ;
33- const initialNames = decodedList
34- . split ( / \n | , | , | \[ | \] / )
35- . map ( ( name ) => name . trim ( ) )
36- . filter ( ( name ) => name ) ;
37- setNames ( initialNames ) ;
38- setMaxSelectable ( initialNames . length ) ;
39- // 记录URL导入名单的历史
40- addNameListHistory ( 'URL导入' , initialNames , `导入${ initialNames . length } 个名字` ) ;
41- }
42- // 加载收藏名单
43- try {
44- const fav = localStorage . getItem ( 'favoriteLists' ) ;
45- if ( fav ) {
46- setFavoriteLists ( JSON . parse ( fav ) ) ;
47- }
48- } catch { }
49- // 加载历史记录
30+ // 先加载历史记录
5031 try {
5132 const drawHist = localStorage . getItem ( 'drawHistory' ) ;
5233 if ( drawHist ) {
@@ -57,6 +38,54 @@ const RandomNamePickerContent = () => {
5738 setNameListHistory ( JSON . parse ( nameHist ) ) ;
5839 }
5940 } catch { }
41+
42+ // 加载收藏名单
43+ try {
44+ const fav = localStorage . getItem ( 'favoriteLists' ) ;
45+ if ( fav ) {
46+ setFavoriteLists ( JSON . parse ( fav ) ) ;
47+ }
48+ } catch { }
49+
50+ // 处理URL参数(只在页面首次加载时)
51+ const list = searchParams . get ( 'list' ) ;
52+ if ( list && names . length === 0 ) { // 只在名单为空时处理,避免重复
53+ const decodedList = decodeURIComponent ( list ) ;
54+ const initialNames = decodedList
55+ . split ( / \n | , | , | \[ | \] / )
56+ . map ( ( name ) => name . trim ( ) )
57+ . filter ( ( name ) => name ) ;
58+
59+ if ( initialNames . length > 0 ) {
60+ setNames ( initialNames ) ;
61+ setMaxSelectable ( initialNames . length ) ;
62+
63+ // 延迟记录历史,确保状态已更新且避免重复
64+ setTimeout ( ( ) => {
65+ const newRecord = {
66+ timestamp : new Date ( ) . toLocaleString ( ) ,
67+ action : 'URL导入' ,
68+ nameList : [ ...initialNames ] ,
69+ details : `导入${ initialNames . length } 个名字`
70+ } ;
71+ setNameListHistory ( prev => {
72+ // 检查是否已经有相同的URL导入记录
73+ const hasUrlImport = prev . some ( record =>
74+ record . action === 'URL导入' &&
75+ record . nameList . length === initialNames . length &&
76+ record . nameList . every ( ( name , index ) => name === initialNames [ index ] )
77+ ) ;
78+
79+ if ( ! hasUrlImport ) {
80+ const newHistory = [ newRecord , ...prev ] . slice ( 0 , 50 ) ;
81+ localStorage . setItem ( 'nameListHistory' , JSON . stringify ( newHistory ) ) ;
82+ return newHistory ;
83+ }
84+ return prev ;
85+ } ) ;
86+ } , 100 ) ;
87+ }
88+ }
6089 } , [ searchParams ] ) ;
6190
6291 // 添加名单历史记录
@@ -68,6 +97,20 @@ const RandomNamePickerContent = () => {
6897 details
6998 } ;
7099 setNameListHistory ( prev => {
100+ // 检查是否与最近的记录重复(避免连续相同操作重复记录)
101+ const lastRecord = prev [ 0 ] ;
102+ if ( lastRecord &&
103+ lastRecord . action === action &&
104+ lastRecord . nameList . length === nameList . length &&
105+ lastRecord . nameList . every ( ( name , index ) => name === nameList [ index ] ) &&
106+ lastRecord . details === details ) {
107+ // 如果完全相同且时间间隔很短(1秒内),则不重复记录
108+ const timeDiff = new Date ( ) . getTime ( ) - new Date ( lastRecord . timestamp ) . getTime ( ) ;
109+ if ( timeDiff < 1000 ) {
110+ return prev ;
111+ }
112+ }
113+
71114 const newHistory = [ newRecord , ...prev ] . slice ( 0 , 50 ) ; // 最多保留50条记录
72115 localStorage . setItem ( 'nameListHistory' , JSON . stringify ( newHistory ) ) ;
73116 return newHistory ;
@@ -83,6 +126,20 @@ const RandomNamePickerContent = () => {
83126 numPicked
84127 } ;
85128 setDrawHistory ( prev => {
129+ // 检查是否与最近的记录重复
130+ const lastRecord = prev [ 0 ] ;
131+ if ( lastRecord &&
132+ lastRecord . nameList . length === nameList . length &&
133+ lastRecord . drawnNames . length === drawnNames . length &&
134+ lastRecord . numPicked === numPicked &&
135+ lastRecord . drawnNames . every ( ( name , index ) => name === drawnNames [ index ] ) ) {
136+ // 如果抽取结果完全相同且时间间隔很短(1秒内),则不重复记录
137+ const timeDiff = new Date ( ) . getTime ( ) - new Date ( lastRecord . timestamp ) . getTime ( ) ;
138+ if ( timeDiff < 1000 ) {
139+ return prev ;
140+ }
141+ }
142+
86143 const newHistory = [ newRecord , ...prev ] . slice ( 0 , 50 ) ; // 最多保留50条记录
87144 localStorage . setItem ( 'drawHistory' , JSON . stringify ( newHistory ) ) ;
88145 return newHistory ;
@@ -129,6 +186,70 @@ const RandomNamePickerContent = () => {
129186 localStorage . setItem ( 'favoriteLists' , JSON . stringify ( newFavs ) ) ;
130187 } ;
131188
189+ // 清空所有历史记录
190+ const handleClearAllHistory = ( ) => {
191+ if ( ! window . confirm ( '确定要清空所有历史记录吗?此操作不可恢复!' ) ) return ;
192+
193+ // 清空状态
194+ setDrawHistory ( [ ] ) ;
195+ setNameListHistory ( [ ] ) ;
196+
197+ // 清空本地存储
198+ localStorage . removeItem ( 'drawHistory' ) ;
199+ localStorage . removeItem ( 'nameListHistory' ) ;
200+
201+ alert ( '所有历史记录已清空!' ) ;
202+ } ;
203+
204+ // 清空抽取历史记录
205+ const handleClearDrawHistory = ( ) => {
206+ if ( ! window . confirm ( '确定要清空抽取历史记录吗?此操作不可恢复!' ) ) return ;
207+
208+ setDrawHistory ( [ ] ) ;
209+ localStorage . removeItem ( 'drawHistory' ) ;
210+
211+ alert ( '抽取历史记录已清空!' ) ;
212+ } ;
213+
214+ // 清空名单变更历史记录
215+ const handleClearNameHistory = ( ) => {
216+ if ( ! window . confirm ( '确定要清空名单变更历史记录吗?此操作不可恢复!' ) ) return ;
217+
218+ setNameListHistory ( [ ] ) ;
219+ localStorage . removeItem ( 'nameListHistory' ) ;
220+
221+ alert ( '名单变更历史记录已清空!' ) ;
222+ } ;
223+
224+ // 从抽取历史还原名单和抽取结果
225+ const handleRestoreFromDrawHistory = ( record : { timestamp : string , nameList : string [ ] , drawnNames : string [ ] , numPicked : number } ) => {
226+ if ( ! window . confirm ( '确定要还原到这次抽取时的状态吗?当前名单将被替换。' ) ) return ;
227+ setNames ( record . nameList ) ;
228+ setSelectedNames ( record . drawnNames ) ;
229+ setNumToPick ( record . numPicked ) ;
230+ setInput ( '' ) ;
231+ setSearchTerm ( '' ) ;
232+ // 还原操作只记录一次简单的记录,避免重复
233+ setTimeout ( ( ) => {
234+ addNameListHistory ( '从抽取历史还原' , record . nameList , `还原到${ record . timestamp } 的抽取状态` ) ;
235+ } , 50 ) ;
236+ alert ( '已还原到该次抽取时的状态!' ) ;
237+ } ;
238+
239+ // 从名单历史还原名单
240+ const handleRestoreFromNameHistory = ( record : { timestamp : string , action : string , nameList : string [ ] , details ?: string } ) => {
241+ if ( ! window . confirm ( '确定要还原到这个时刻的名单吗?当前名单将被替换。' ) ) return ;
242+ setNames ( record . nameList ) ;
243+ setSelectedNames ( [ ] ) ;
244+ setInput ( '' ) ;
245+ setSearchTerm ( '' ) ;
246+ // 还原操作只记录一次简单的记录,避免重复
247+ setTimeout ( ( ) => {
248+ addNameListHistory ( '从名单历史还原' , record . nameList , `还原到${ record . timestamp } 的名单状态` ) ;
249+ } , 50 ) ;
250+ alert ( '已还原到该时刻的名单状态!' ) ;
251+ } ;
252+
132253 useEffect ( ( ) => {
133254 setMaxSelectable ( names . length || 1 ) ;
134255 if ( numToPick > names . length ) {
@@ -386,16 +507,52 @@ const RandomNamePickerContent = () => {
386507 </ button >
387508 </ div >
388509
510+ { /* 历史记录总控制区域 */ }
511+ < div className = "history-control-section" >
512+ < div className = "list-header" >
513+ < h2 > 历史记录管理</ h2 >
514+ < button
515+ onClick = { handleClearAllHistory }
516+ className = "button danger"
517+ disabled = { drawHistory . length === 0 && nameListHistory . length === 0 }
518+ style = { { padding : '8px 16px' , fontSize : '14px' } }
519+ >
520+ 清空所有历史记录
521+ </ button >
522+ </ div >
523+ </ div >
524+
389525 { /* 历史记录部分 */ }
390526 < div className = "history-section" >
391- < h2 > 抽取历史记录</ h2 >
527+ < div className = "list-header" >
528+ < h2 > 抽取历史记录</ h2 >
529+ < div className = "button-group" >
530+ < button
531+ onClick = { handleClearDrawHistory }
532+ className = "button danger"
533+ disabled = { drawHistory . length === 0 }
534+ style = { { padding : '6px 12px' , fontSize : '14px' } }
535+ >
536+ 清空抽取历史
537+ </ button >
538+ </ div >
539+ </ div >
392540 { drawHistory . length > 0 ? (
393541 < div className = "history-container" >
394542 { drawHistory . slice ( 0 , 10 ) . map ( ( record , index ) => (
395543 < div key = { index } className = "history-item" >
396544 < div className = "history-header" >
397545 < span className = "history-time" > { record . timestamp } </ span >
398- < span className = "history-info" > 从 { record . nameList . length } 人中抽取 { record . numPicked } 人</ span >
546+ < div >
547+ < span className = "history-info" > 从 { record . nameList . length } 人中抽取 { record . numPicked } 人</ span >
548+ < button
549+ onClick = { ( ) => handleRestoreFromDrawHistory ( record ) }
550+ className = "button primary"
551+ style = { { marginLeft : 8 , padding : '2px 8px' , fontSize : '12px' } }
552+ >
553+ 还原此状态
554+ </ button >
555+ </ div >
399556 </ div >
400557 < div className = "history-content" >
401558 < div > 当时名单: { record . nameList . join ( ', ' ) } </ div >
@@ -410,14 +567,35 @@ const RandomNamePickerContent = () => {
410567 </ div >
411568
412569 < div className = "history-section" >
413- < h2 > 名单变更历史</ h2 >
570+ < div className = "list-header" >
571+ < h2 > 名单变更历史</ h2 >
572+ < div className = "button-group" >
573+ < button
574+ onClick = { handleClearNameHistory }
575+ className = "button danger"
576+ disabled = { nameListHistory . length === 0 }
577+ style = { { padding : '6px 12px' , fontSize : '14px' } }
578+ >
579+ 清空变更历史
580+ </ button >
581+ </ div >
582+ </ div >
414583 { nameListHistory . length > 0 ? (
415584 < div className = "history-container" >
416585 { nameListHistory . slice ( 0 , 10 ) . map ( ( record , index ) => (
417586 < div key = { index } className = "history-item" >
418587 < div className = "history-header" >
419588 < span className = "history-time" > { record . timestamp } </ span >
420- < span className = "history-action" > { record . action } </ span >
589+ < div >
590+ < span className = "history-action" > { record . action } </ span >
591+ < button
592+ onClick = { ( ) => handleRestoreFromNameHistory ( record ) }
593+ className = "button primary"
594+ style = { { marginLeft : 8 , padding : '2px 8px' , fontSize : '12px' } }
595+ >
596+ 还原名单
597+ </ button >
598+ </ div >
421599 </ div >
422600 < div className = "history-content" >
423601 { record . details && < div style = { { fontSize : '14px' , color : '#666' } } > { record . details } </ div > }
@@ -445,7 +623,7 @@ const RandomNamePickerContent = () => {
445623 className = "search-input"
446624 style = { { width : 180 , marginRight : 8 } }
447625 />
448- < button onClick = { handleSaveFavorite } className = "button secondary " >
626+ < button onClick = { handleSaveFavorite } className = "button primary " >
449627 收藏当前名单
450628 </ button >
451629 </ div >
@@ -546,7 +724,7 @@ const RandomNamePickerContent = () => {
546724 font-size: 2.2rem;
547725 }
548726
549- .input-section, .list-section, .picker-section, .range-section, .history-section, .favorite-section {
727+ .input-section, .list-section, .picker-section, .range-section, .history-section, .favorite-section, .history-control-section {
550728 margin-bottom: 30px;
551729 }
552730
@@ -761,6 +939,8 @@ const RandomNamePickerContent = () => {
761939 justify-content: space-between;
762940 align-items: center;
763941 margin-bottom: 8px;
942+ flex-wrap: wrap;
943+ gap: 8px;
764944 }
765945
766946 .history-time {
0 commit comments