@@ -247,7 +247,13 @@ const fetchCCWExtensions = async (name, sortField, page, perPage) => {
247247 }
248248 const json = await response . json ( ) ;
249249 if ( json ?. body ?. data ) {
250- return json . body . data ;
250+ const body = json . body ;
251+ return {
252+ items : body . data ,
253+ total : body . total || body . totalCount || body . count || null ,
254+ page : body . page || page || 1 ,
255+ perPage : body . perPage || perPage || 30
256+ } ;
251257 }
252258 throw new Error ( 'CCW API response format unexpected' ) ;
253259} ;
@@ -638,6 +644,7 @@ class ExtensionLibrary extends React.PureComponent {
638644 bindAll ( this , [
639645 'executeItemAction' ,
640646 'fetchAndSetCCWItems' ,
647+ 'getCCWRecommendationButtons' ,
641648 'getActionLabel' ,
642649 'getBatchSelectableItems' ,
643650 'getCardProps' ,
@@ -651,6 +658,8 @@ class ExtensionLibrary extends React.PureComponent {
651658 'getSections' ,
652659 'handleBatchImport' ,
653660 'handleCCWSortChange' ,
661+ 'handleCCWPageChange' ,
662+ 'handleCCWRecommendationSelect' ,
654663 'handleClearFilters' ,
655664 'handleClearQuery' ,
656665 'handleClearSelection' ,
@@ -678,7 +687,11 @@ class ExtensionLibrary extends React.PureComponent {
678687 galleryTimedOut : false ,
679688 ccwItems : [ ] ,
680689 ccwLoading : false ,
681- ccwSortField : 'updatedAt' ,
690+ ccwSortField : 'likeCount' ,
691+ ccwPage : 1 ,
692+ ccwPerPage : 30 ,
693+ ccwHasMore : false ,
694+ ccwTotal : null ,
682695 query : '' ,
683696 quickFilters : {
684697 favorites : false ,
@@ -717,17 +730,27 @@ class ExtensionLibrary extends React.PureComponent {
717730 } ) ;
718731 }
719732 // 初始加载CCW扩展
720- this . fetchAndSetCCWItems ( '' , 'updatedAt' ) ;
733+ this . fetchAndSetCCWItems ( '' , 'likeCount' , 1 ) ;
721734 }
722- async fetchAndSetCCWItems ( name , sortField ) {
735+ async fetchAndSetCCWItems ( name , sortField , page = 1 ) {
723736 this . setState ( { ccwLoading : true } ) ;
724737 try {
725- const rawItems = await fetchCCWExtensions ( name , sortField ) ;
738+ const result = await fetchCCWExtensions ( name , sortField , page , this . state . ccwPerPage ) ;
739+ const rawItems = Array . isArray ( result . items ) ? result . items : [ ] ;
726740 const ccwItems = rawItems . map ( item => toCCWGalleryItem ( item ) ) ;
727- this . setState ( { ccwItems, ccwLoading : false } ) ;
741+ const hasMore = typeof result . total === 'number' ?
742+ ( page * result . perPage ) < result . total :
743+ rawItems . length >= result . perPage ;
744+ this . setState ( {
745+ ccwItems,
746+ ccwLoading : false ,
747+ ccwPage : page ,
748+ ccwHasMore : hasMore ,
749+ ccwTotal : result . total
750+ } ) ;
728751 } catch ( err ) {
729752 log . error ( err ) ;
730- this . setState ( { ccwItems : [ ] , ccwLoading : false } ) ;
753+ this . setState ( { ccwItems : [ ] , ccwLoading : false , ccwHasMore : false , ccwTotal : null } ) ;
731754 }
732755 }
733756 readFavoritesFromStorage ( ) {
@@ -1062,16 +1085,30 @@ class ExtensionLibrary extends React.PureComponent {
10621085 }
10631086 handleCCWSortChange ( event ) {
10641087 const sortField = event . target . value ;
1065- this . setState ( { ccwSortField : sortField } ) ;
1066- this . fetchAndSetCCWItems ( this . state . query , sortField ) ;
1088+ this . setState ( { ccwSortField : sortField , ccwPage : 1 } ) ;
1089+ this . fetchAndSetCCWItems ( this . state . query , sortField , 1 ) ;
1090+ }
1091+ handleCCWRecommendationSelect ( sortField ) {
1092+ this . setState ( { ccwSortField : sortField , ccwPage : 1 } ) ;
1093+ this . fetchAndSetCCWItems ( this . state . query , sortField , 1 ) ;
1094+ }
1095+ handleCCWPageChange ( delta ) {
1096+ const nextPage = Math . max ( 1 , this . state . ccwPage + delta ) ;
1097+ if ( nextPage === this . state . ccwPage ) {
1098+ return ;
1099+ }
1100+ if ( delta > 0 && ! this . state . ccwHasMore ) {
1101+ return ;
1102+ }
1103+ this . fetchAndSetCCWItems ( this . state . query , this . state . ccwSortField , nextPage ) ;
10671104 }
10681105 handleSourceSelect ( selectedSource ) {
10691106 this . setState ( {
10701107 selectedSource
10711108 } ) ;
10721109 // 切换到CCW时触发实时搜索
10731110 if ( selectedSource === SOURCE_KEYS . CCW || selectedSource === SOURCE_KEYS . ALL ) {
1074- this . fetchAndSetCCWItems ( this . state . query , this . state . ccwSortField ) ;
1111+ this . fetchAndSetCCWItems ( this . state . query , this . state . ccwSortField , 1 ) ;
10751112 }
10761113 }
10771114 handleQueryChange ( query ) {
@@ -1083,7 +1120,7 @@ class ExtensionLibrary extends React.PureComponent {
10831120 clearTimeout ( this . _queryTimeout ) ;
10841121 }
10851122 this . _queryTimeout = setTimeout ( ( ) => {
1086- this . fetchAndSetCCWItems ( query , this . state . ccwSortField ) ;
1123+ this . fetchAndSetCCWItems ( query , this . state . ccwSortField , 1 ) ;
10871124 } , 500 ) ;
10881125 }
10891126 handleToggleQuickFilter ( filterKey ) {
@@ -1098,7 +1135,7 @@ class ExtensionLibrary extends React.PureComponent {
10981135 this . setState ( {
10991136 query : ''
11001137 } ) ;
1101- this . fetchAndSetCCWItems ( '' , 'updatedAt' ) ;
1138+ this . fetchAndSetCCWItems ( '' , this . state . ccwSortField , 1 ) ;
11021139 }
11031140 handleClearFilters ( ) {
11041141 this . setState ( {
@@ -1110,9 +1147,10 @@ class ExtensionLibrary extends React.PureComponent {
11101147 native : false ,
11111148 custom : false
11121149 } ,
1113- selectedSource : SOURCE_KEYS . ALL
1150+ selectedSource : SOURCE_KEYS . ALL ,
1151+ ccwPage : 1
11141152 } ) ;
1115- this . fetchAndSetCCWItems ( '' , 'updatedAt' ) ;
1153+ this . fetchAndSetCCWItems ( '' , this . state . ccwSortField , 1 ) ;
11161154 }
11171155 handleCustomExtensionOpen ( ) {
11181156 this . props . onOpenCustomExtensionModal ( ) ;
@@ -1296,49 +1334,57 @@ class ExtensionLibrary extends React.PureComponent {
12961334 sourceTone : sourceToneMap [ item . source ] || 'Other'
12971335 } ;
12981336 }
1337+ getCCWRecommendationButtons ( ) {
1338+ const options = [
1339+ [ 'likeCount' , '最多喜欢' ] ,
1340+ [ 'updatedAt' , '最近更新' ] ,
1341+ [ 'createdAt' , '最新创建' ] ,
1342+ [ 'donateCount' , '最多投币' ]
1343+ ] ;
1344+ return options . map ( ( [ value , label ] ) => (
1345+ < button
1346+ key = { value }
1347+ type = "button"
1348+ className = { [
1349+ libraryStyles . quickFilterButton ,
1350+ this . state . ccwSortField === value ? libraryStyles . quickFilterButtonActive : ''
1351+ ] . join ( ' ' ) }
1352+ onClick = { ( ) => this . handleCCWRecommendationSelect ( value ) }
1353+ >
1354+ { label }
1355+ </ button >
1356+ ) ) ;
1357+ }
12991358 getCCWSortControl ( ) {
13001359 return (
13011360 < div style = { { marginTop : '0.75rem' } } >
13021361 < div className = { libraryStyles . sidebarTitle } style = { { marginBottom : '0.5rem' , opacity : 0.6 , fontSize : '0.75rem' } } >
1303- 排序方式
1362+ 推荐方式
1363+ </ div >
1364+ < div className = { libraryStyles . quickFilters } >
1365+ { this . getCCWRecommendationButtons ( ) }
1366+ </ div >
1367+ < div className = { libraryStyles . sidebarTitle } style = { { marginTop : '0.75rem' , marginBottom : '0.5rem' , opacity : 0.6 , fontSize : '0.75rem' } } >
1368+ 第 { this . state . ccwPage } 页{ typeof this . state . ccwTotal === 'number' ? ` / 共 ${ this . state . ccwTotal } 个` : '' }
1369+ </ div >
1370+ < div className = { libraryStyles . quickFilters } >
1371+ < button
1372+ type = "button"
1373+ className = { libraryStyles . quickFilterButton }
1374+ disabled = { this . state . ccwPage <= 1 || this . state . ccwLoading }
1375+ onClick = { ( ) => this . handleCCWPageChange ( - 1 ) }
1376+ >
1377+ 上一页
1378+ </ button >
1379+ < button
1380+ type = "button"
1381+ className = { libraryStyles . quickFilterButton }
1382+ disabled = { ! this . state . ccwHasMore || this . state . ccwLoading }
1383+ onClick = { ( ) => this . handleCCWPageChange ( 1 ) }
1384+ >
1385+ 下一页
1386+ </ button >
13041387 </ div >
1305- < button
1306- type = "button"
1307- className = { [
1308- libraryStyles . sidebarButton ,
1309- libraryStyles . sidebarActionButton
1310- ] . join ( ' ' ) }
1311- style = { { cursor : 'default' , background : '#00baad' } }
1312- >
1313- < div className = { libraryStyles . sidebarButtonLabel } >
1314- < select
1315- value = { this . state . ccwSortField }
1316- onChange = { this . handleCCWSortChange }
1317- style = { {
1318- width : '100%' ,
1319- appearance : 'none' ,
1320- WebkitAppearance : 'none' ,
1321- MozAppearance : 'none' ,
1322- padding : 0 ,
1323- border : 'none' ,
1324- borderRadius : 0 ,
1325- fontSize : 'inherit' ,
1326- fontWeight : 'inherit' ,
1327- color : 'inherit' ,
1328- outline : 'none' ,
1329- backgroundColor : 'transparent' ,
1330- cursor : 'pointer' ,
1331- fontFamily : 'inherit' ,
1332- textAlign : 'left'
1333- } }
1334- >
1335- < option value = "updatedAt" > 最近更新</ option >
1336- < option value = "likeCount" > 最多喜欢</ option >
1337- < option value = "donateCount" > 最多投币</ option >
1338- < option value = "createdAt" > 最新创建</ option >
1339- </ select >
1340- </ div >
1341- </ button >
13421388 </ div >
13431389 ) ;
13441390 }
@@ -1497,4 +1543,4 @@ ExtensionLibrary.propTypes = {
14971543 vm : PropTypes . instanceOf ( VM ) . isRequired
14981544} ;
14991545
1500- export default injectIntl ( ExtensionLibrary ) ;
1546+ export default injectIntl ( ExtensionLibrary ) ;
0 commit comments