@@ -152,7 +152,7 @@ import { Toast } from "@nutui/nutui";
152152import { useDebounceFn } from " @vueuse/core" ;
153153import axios from " axios" ;
154154import { storeToRefs } from " pinia" ;
155- import { computed , reactive , ref , watch } from " vue" ;
155+ import { computed , onBeforeUnmount , reactive , ref , watch } from " vue" ;
156156import { useI18n } from " vue-i18n" ;
157157
158158import { useGlobalStore } from " @/store/global" ;
@@ -173,6 +173,10 @@ const { customIconCollections, defaultIconCollections, defaultIconCollection } =
173173 storeToRefs (globalStore );
174174const { githubProxy, githubProxyRegex } = storeToRefs (settingsStore );
175175
176+ const ICON_COLLECTION_FETCH_RETRY_COUNT = 1 ;
177+ const ICON_COLLECTION_FETCH_RETRY_DELAY = 600 ;
178+ const ICON_COLLECTION_INITIAL_FETCH_DELAY = 350 ;
179+
176180const form = reactive ({
177181 iconName: " " ,
178182 iconCollectionName: " " ,
@@ -238,6 +242,28 @@ const defaultIconCollectionValue = ref([""]);
238242const showIconCollectionPicker = ref (false );
239243
240244const showCustomIconCollection = ref (false );
245+ let initialFetchTimer: ReturnType <typeof setTimeout > | null = null ;
246+
247+ const clearInitialFetchTimer = () => {
248+ if (initialFetchTimer ) {
249+ clearTimeout (initialFetchTimer );
250+ initialFetchTimer = null ;
251+ }
252+ };
253+
254+ const scheduleInitialFetch = () => {
255+ clearInitialFetchTimer ();
256+ initialFetchTimer = setTimeout (() => {
257+ initialFetchTimer = null ;
258+ if (! isVisible .value ) {
259+ return ;
260+ }
261+
262+ refreshIcons ();
263+ }, ICON_COLLECTION_INITIAL_FETCH_DELAY );
264+ };
265+
266+ onBeforeUnmount (clearInitialFetchTimer );
241267
242268const setDefaultIconCollection = (url : string ) => {
243269 globalStore .setDefaultIconCollection (url );
@@ -247,14 +273,61 @@ const setCustomIconCollections = (collection: any[]) => {
247273 globalStore .setCustomIconCollections (collection );
248274};
249275
276+ const getFallbackIconCollectionUrl = () => {
277+ return defaultIconCollections .value [0 ]?.value || " " ;
278+ };
279+
280+ const isKnownIconCollectionUrl = (url : string ) => {
281+ return iconCollectionColumns .value .some ((item ) => item .value === url );
282+ };
283+
284+ const setCurrentIconCollectionUrl = (url : string ) => {
285+ form .iconCollectionUrl = url ;
286+ defaultIconCollectionValue .value [0 ] = url ;
287+ };
288+
289+ let iconFetchRequestId = 0 ;
290+
291+ const wait = (ms : number ) => new Promise ((resolve ) => setTimeout (resolve , ms ));
292+
293+ const fetchIconCollection = async (url : string ) => {
294+ let lastError;
295+
296+ for (let attempt = 0 ; attempt <= ICON_COLLECTION_FETCH_RETRY_COUNT ; attempt ++ ) {
297+ try {
298+ const { data } = await axios .get (url );
299+ return data ;
300+ } catch (error ) {
301+ lastError = error ;
302+
303+ if (attempt < ICON_COLLECTION_FETCH_RETRY_COUNT ) {
304+ await wait (ICON_COLLECTION_FETCH_RETRY_DELAY );
305+ }
306+ }
307+ }
308+
309+ throw lastError ;
310+ };
311+
250312const fetchIcons = async () => {
313+ const requestId = iconFetchRequestId + 1 ;
314+ iconFetchRequestId = requestId ;
315+ const iconCollectionUrl = form .iconCollectionUrl ;
316+
251317 try {
252318 Toast .loading (t (` globalNotify.refresh.loading ` ), {
253319 cover: true ,
254320 id: " icon-collection" ,
255321 });
256322 fetchStatus .value = " loading" ;
257- const { data } = await axios .get (rewriteGithubUrl (form .iconCollectionUrl ));
323+ const rewrittenIconCollectionUrl = rewriteGithubUrl (iconCollectionUrl );
324+ if (! rewrittenIconCollectionUrl ) {
325+ throw new Error (" Missing icon collection URL" );
326+ }
327+ const data = await fetchIconCollection (rewrittenIconCollectionUrl );
328+ if (requestId !== iconFetchRequestId ) {
329+ return ;
330+ }
258331 const collectionKey = form .iconListKey || " icons" ;
259332 const iconUrlKey = form .iconItemUrlKey || " url" ;
260333 iconList .value = data [collectionKey ];
@@ -271,23 +344,23 @@ const fetchIcons = async () => {
271344 });
272345 // 添加自定义图标仓库,
273346 const hasCollection = iconCollectionColumns .value .some (
274- (item ) => item .value === form . iconCollectionUrl ,
347+ (item ) => item .value === iconCollectionUrl ,
275348 );
276349 console .log (" hasCollection" , hasCollection );
277350 if (! hasCollection ) {
278351 const list = [
279352 {
280353 text: name ,
281- value: form . iconCollectionUrl ,
354+ value: iconCollectionUrl ,
282355 },
283356 ... customIconCollections .value ,
284357 ];
285358 setCustomIconCollections (list );
286- setDefaultIconCollection (form . iconCollectionUrl );
287- defaultIconCollectionValue .value [0 ] = form . iconCollectionUrl ;
359+ setDefaultIconCollection (iconCollectionUrl );
360+ defaultIconCollectionValue .value [0 ] = iconCollectionUrl ;
288361 } else {
289- setDefaultIconCollection (form . iconCollectionUrl );
290- defaultIconCollectionValue .value [0 ] = form . iconCollectionUrl ;
362+ setDefaultIconCollection (iconCollectionUrl );
363+ defaultIconCollectionValue .value [0 ] = iconCollectionUrl ;
291364 }
292365 } else {
293366 iconList .value = [];
@@ -297,6 +370,9 @@ const fetchIcons = async () => {
297370 fetchStatus .value = " success" ;
298371 Toast .hide (" icon-collection" );
299372 } catch (error ) {
373+ if (requestId !== iconFetchRequestId ) {
374+ return ;
375+ }
300376 Toast .hide (" icon-collection" );
301377 iconList .value = [];
302378 searchResult .value = [];
@@ -338,13 +414,22 @@ const handleCancel = () => {
338414};
339415
340416const syncIconCollectionState = () => {
341- if (defaultIconCollection .value ) {
342- form .iconCollectionUrl = defaultIconCollection .value ;
343- defaultIconCollectionValue .value [0 ] = defaultIconCollection .value ;
344- } else {
345- form .iconCollectionUrl = defaultIconCollections .value [0 ].value ;
346- defaultIconCollectionValue .value [0 ] = defaultIconCollections .value [0 ].value ;
417+ const savedIconCollectionUrl = defaultIconCollection .value ;
418+ const fallbackIconCollectionUrl = getFallbackIconCollectionUrl ();
419+ const iconCollectionUrl =
420+ savedIconCollectionUrl && isKnownIconCollectionUrl (savedIconCollectionUrl )
421+ ? savedIconCollectionUrl
422+ : fallbackIconCollectionUrl ;
423+
424+ if (! iconCollectionUrl ) {
425+ return ;
426+ }
427+
428+ if (savedIconCollectionUrl && savedIconCollectionUrl !== iconCollectionUrl ) {
429+ setDefaultIconCollection (iconCollectionUrl );
347430 }
431+
432+ setCurrentIconCollectionUrl (iconCollectionUrl );
348433};
349434
350435const clearIconName = () => {
@@ -384,7 +469,10 @@ watch(
384469 isVisible .value = newValue ;
385470 if (newValue ) {
386471 syncIconCollectionState ();
387- refreshIcons ();
472+ fetchStatus .value = form .iconCollectionUrl ? " loading" : " idle" ;
473+ scheduleInitialFetch ();
474+ } else {
475+ clearInitialFetchTimer ();
388476 }
389477 },
390478 { immediate: true },
@@ -399,6 +487,7 @@ const close = () => {
399487 hide ();
400488};
401489const hide = () => {
490+ clearInitialFetchTimer ();
402491 isVisible .value = false ;
403492 emit (" update:visible" , false );
404493};
@@ -460,6 +549,7 @@ defineExpose({ show, hide, close });
460549 padding-left : 60px ;
461550 flex-shrink : 0 ;
462551 .action-btn {
552+ cursor : pointer ;
463553 display : flex ;
464554 align-items : center ;
465555 justify-content : flex-end ;
0 commit comments