@@ -204,9 +204,9 @@ export class ElementAssetTreeComponent implements OnInit {
204204 ] ;
205205 }
206206
207- // Asset node inside a favorite folder: connect / remove favorite
208- if ( cnode . favoriteFolderId ) {
209- const isK8sFav = cnode . meta . data . platform_type === 'k8s' ;
207+ // Favorite asset leaf (under a folder or directly under root) : connect / move / remove
208+ if ( cnode . isFavoriteLeaf ) {
209+ const isK8sFav = cnode . meta ? .data ? .platform_type === 'k8s' ;
210210 const favViewList = this . _viewSrv . viewList ;
211211 return [
212212 {
@@ -230,6 +230,7 @@ export class ElementAssetTreeComponent implements OnInit {
230230 hide : favViewList . length <= 0 || isK8sFav ,
231231 click : this . onFavoriteConnect . bind ( this , true )
232232 } ,
233+ ...this . getMoveToMenus ( cnode ) ,
233234 {
234235 id : 'remove-favorite' ,
235236 name : this . _i18n . instant ( 'Remove favorite' ) ,
@@ -324,12 +325,45 @@ export class ElementAssetTreeComponent implements OnInit {
324325 if ( cnode . isParent ) {
325326 return [ ] ;
326327 }
328+ return this . buildFolderTargetMenus ( {
329+ id : 'favorite-to' ,
330+ name : this . _i18n . instant ( 'Favorite to' ) ,
331+ fa : 'fa-star-o' ,
332+ assetId : this . resolveAssetId ( cnode ) ,
333+ onSelect : this . onFavoriteTo . bind ( this )
334+ } ) ;
335+ }
336+
337+ /**
338+ * Build a "Move to" parent menu for a favorite-tree asset leaf,
339+ * so it can be relocated into another favorite folder.
340+ */
341+ getMoveToMenus ( cnode ) : any [ ] {
342+ return this . buildFolderTargetMenus ( {
343+ id : 'move-to' ,
344+ name : this . _i18n . instant ( 'Move to' ) ,
345+ fa : 'fa-folder-o' ,
346+ assetId : this . resolveAssetId ( cnode ) ,
347+ onSelect : this . onMoveTo . bind ( this )
348+ } ) ;
349+ }
350+
351+ /**
352+ * Shared folder submenu builder for "Favorite to" / "Move to".
353+ */
354+ private buildFolderTargetMenus ( options : {
355+ id : string ;
356+ name : string ;
357+ fa : string ;
358+ assetId : string ;
359+ onSelect : ( folderId : string ) => void ;
360+ } ) : any [ ] {
327361 if ( this . favoriteFolders . length === 0 ) {
328362 return [
329363 {
330- id : 'favorite-to' ,
331- name : this . _i18n . instant ( 'Favorite to' ) ,
332- fa : 'fa-star-o' ,
364+ id : options . id ,
365+ name : options . name ,
366+ fa : options . fa ,
333367 hide : false ,
334368 children : [
335369 {
@@ -341,21 +375,20 @@ export class ElementAssetTreeComponent implements OnInit {
341375 }
342376 ] ;
343377 }
344- const assetId = this . resolveAssetId ( cnode ) ;
345- const favoritedFolderIds = this . getAssetFavoriteFolderIds ( assetId ) ;
378+ const favoritedFolderIds = this . getAssetFavoriteFolderIds ( options . assetId ) ;
346379 return [
347380 {
348- id : 'favorite-to' ,
349- name : this . _i18n . instant ( 'Favorite to' ) ,
350- fa : 'fa-star-o' ,
381+ id : options . id ,
382+ name : options . name ,
383+ fa : options . fa ,
351384 hide : false ,
352385 children : this . favoriteFolders . map ( folder => {
353386 const folderId = this . resolveFolderId ( folder ) ;
354387 return {
355- id : 'favorite-to -' + folderId ,
388+ id : options . id + ' -' + folderId ,
356389 name : folder . name ,
357390 checked : favoritedFolderIds . has ( folderId ) ,
358- click : this . onFavoriteTo . bind ( this , folderId )
391+ click : ( ) => options . onSelect ( folderId )
359392 } ;
360393 } )
361394 }
@@ -636,23 +669,27 @@ export class ElementAssetTreeComponent implements OnInit {
636669 favoriteFolderRealId : folderId
637670 } ) ;
638671 } ) ;
639- // Asset leaf nodes (one asset belongs to one folder; leaf id still uses folder+asset)
672+ // Asset leaf nodes: under folder when folder is set, otherwise under favorite root
640673 ( favorites || [ ] ) . forEach ( fav => {
641- const folderId = this . resolveFolderId ( fav . folder ?? fav . folder_id ) ;
642- if ( ! folderId || ! fav . asset_info ) {
674+ if ( ! fav . asset_info ) {
643675 return ;
644676 }
677+ const folderId = this . resolveFolderId ( fav . folder ?? fav . folder_id ) ;
645678 const info = fav . asset_info ;
646679 const assetId = this . resolveAssetId ( { id : info . id , assetId : info . id , meta : info . meta } ) ;
680+ if ( ! assetId ) {
681+ return ;
682+ }
647683 nodes . push ( {
648- id : this . buildFavoriteLeafId ( folderId , assetId ) ,
649- pId : 'folder-' + folderId ,
684+ id : this . buildFavoriteLeafId ( folderId || 'root' , assetId ) ,
685+ pId : folderId ? 'folder-' + folderId : 'favorite-root' ,
650686 name : info . name ,
651687 isParent : false ,
652688 iconSkin : info . iconSkin ,
653689 chkDisabled : info . chkDisabled ,
654690 assetId,
655- favoriteFolderId : folderId ,
691+ favoriteFolderId : folderId || null ,
692+ isFavoriteLeaf : true ,
656693 meta : info . meta
657694 } ) ;
658695 } ) ;
@@ -1088,7 +1125,7 @@ export class ElementAssetTreeComponent implements OnInit {
10881125 this . refreshFavoriteTree ( ) ;
10891126 return ;
10901127 }
1091- return this . favoriteAssetToFolder ( createdFolderId , favoriteNode , {
1128+ return this . favoriteAssetToFolder ( createdFolderId , favoriteNode , 'Favorite' , {
10921129 refreshTreeOnSuccess : true
10931130 } ) . catch ( ( ) => {
10941131 this . refreshFavoriteTree ( ) ;
@@ -1208,19 +1245,30 @@ export class ElementAssetTreeComponent implements OnInit {
12081245 * @param folderId target folder id
12091246 */
12101247 onFavoriteTo ( folderId : string ) {
1211- this . favoriteAssetToFolder ( folderId , this . rightClickSelectNode ) . catch ( ( ) => { } ) ;
1248+ this . favoriteAssetToFolder ( folderId , this . rightClickSelectNode , 'Favorite' ) . catch ( ( ) => { } ) ;
1249+ }
1250+
1251+ /** Move a favorite-tree asset leaf into another folder. */
1252+ onMoveTo ( folderId : string ) {
1253+ this . favoriteAssetToFolder ( folderId , this . rightClickSelectNode , 'Move' ) . catch ( ( ) => { } ) ;
12121254 }
12131255
12141256 private favoriteAssetToFolder (
12151257 folderId : string ,
12161258 srcNode : any ,
1259+ actionKey : string = 'Favorite' ,
12171260 options : { refreshTreeOnSuccess ?: boolean } = { }
12181261 ) : Promise < void > {
12191262 const normalizedFolderId = this . resolveFolderId ( folderId ) ;
12201263 const assetId = this . resolveAssetId ( srcNode ) ;
12211264 if ( ! normalizedFolderId || ! assetId ) {
12221265 return Promise . resolve ( ) ;
12231266 }
1267+ const currentFolderId = this . resolveFolderId ( srcNode . favoriteFolderId ) ;
1268+ if ( currentFolderId && currentFolderId === normalizedFolderId ) {
1269+ this . _message . warning ( this . _i18n . instant ( 'Already in this folder' ) ) ;
1270+ return Promise . resolve ( ) ;
1271+ }
12241272 const favoritingKey = `${ assetId } -${ normalizedFolderId } ` ;
12251273 if ( this . favoritingInFlight . has ( favoritingKey ) ) {
12261274 return Promise . resolve ( ) ;
@@ -1230,7 +1278,7 @@ export class ElementAssetTreeComponent implements OnInit {
12301278 . favoriteAssetToFolder ( assetId , normalizedFolderId )
12311279 . toPromise ( )
12321280 . then ( ( ) => {
1233- const msg = this . _i18n . instant ( 'Favorite' ) + ' ' + this . _i18n . instant ( 'success' ) ;
1281+ const msg = this . _i18n . instant ( actionKey ) + ' ' + this . _i18n . instant ( 'success' ) ;
12341282 this . _toastr . success ( msg , '' , { nzClass : 'custom-success-notification' } ) ;
12351283 this . setFavoriteAssetRecord ( normalizedFolderId , assetId , srcNode ) ;
12361284 if ( options . refreshTreeOnSuccess ) {
@@ -1291,22 +1339,26 @@ export class ElementAssetTreeComponent implements OnInit {
12911339 chkDisabled : srcNode . chkDisabled ,
12921340 assetId : normalizedAssetId ,
12931341 favoriteFolderId : normalizedFolderId ,
1342+ isFavoriteLeaf : true ,
12941343 meta : srcNode . meta
12951344 }
12961345 ] ) ;
12971346 }
12981347
12991348 /**
1300- * Remove an asset from its folder.
1349+ * Remove an asset from favorites (from a folder, or from root when folder is empty) .
13011350 * On success, only remove this single leaf node (instantly disappears),
13021351 * without rebuilding the whole tree, keeping other folders' expand state.
13031352 */
13041353 onRemoveFromFolder ( ) {
13051354 const node = this . rightClickSelectNode ;
13061355 const assetId = node . assetId ;
1307- const folderId = node . favoriteFolderId ;
1356+ const folderId = this . resolveFolderId ( node . favoriteFolderId ) ;
13081357 const ztree = node . ztree ;
1309- this . _http . removeFavoriteFromFolder ( assetId , folderId ) . subscribe ( ( ) => {
1358+ const remove$ = folderId
1359+ ? this . _http . removeFavoriteFromFolder ( assetId , folderId )
1360+ : this . _http . favoriteAsset ( assetId , false ) ;
1361+ remove$ . subscribe ( ( ) => {
13101362 this . removeFavoriteAssetRecord ( folderId , assetId ) ;
13111363 const msg = this . _i18n . instant ( 'Remove favorite' ) + ' ' + this . _i18n . instant ( 'success' ) ;
13121364 this . _toastr . success ( msg , '' , { nzClass : 'custom-success-notification' } ) ;
0 commit comments