Browse Source

fix: iframe spinner

vben 9 months ago
parent
commit
fcf4b9ae5b

+ 12 - 7
apps/web-antd/src/main.ts

@@ -23,7 +23,7 @@ async function initApplication() {
   await bootstrap(namespace);
 
   // 移除并销毁loading
-  destoryAppLoading();
+  destroyAppLoading();
 }
 
 /**
@@ -31,23 +31,28 @@ async function initApplication() {
  * 放在这里是而不是放在 index.html 的app标签内,主要是因为这样比较不会生硬,渲染过快可能会有闪烁
  * 通过先添加css动画隐藏,在动画结束后在移除loading节点来改善体验
  */
-function destoryAppLoading() {
-  // 全局搜索文件 loading.html, 找到对应的节点
+function destroyAppLoading() {
+  // 查找全局 loading 元素
   const loadingElement = document.querySelector('#__app-loading__');
+
   if (loadingElement) {
+    // 添加隐藏类,触发过渡动画
     loadingElement.classList.add('hidden');
+
+    // 查找所有需要移除的注入 loading 元素
     const injectLoadingElements = document.querySelectorAll(
       '[data-app-loading^="inject"]',
     );
-    // 过渡动画结束后移除loading节点
+
+    // 当过渡动画结束时,移除 loading 元素和所有注入的 loading 元素
     loadingElement.addEventListener(
       'transitionend',
       () => {
-        loadingElement.remove();
-        injectLoadingElements.forEach((el) => el?.remove());
+        loadingElement.remove(); // 移除 loading 元素
+        injectLoadingElements.forEach((el) => el.remove()); // 移除所有注入的 loading 元素
       },
       { once: true },
-    );
+    ); // 确保事件只触发一次
   }
 }
 

+ 1 - 0
packages/@core/uikit/shadcn-ui/src/components/index.ts

@@ -19,6 +19,7 @@ export * from './pin-input';
 export * from './popover';
 export * from './segmented';
 export * from './sheet';
+export * from './spinner';
 export * from './tooltip';
 export * from './ui/alert-dialog';
 export * from './ui/avatar';

+ 0 - 0
packages/business/common-ui/src/spinner/index.ts → packages/@core/uikit/shadcn-ui/src/components/spinner/index.ts


+ 16 - 6
packages/business/common-ui/src/spinner/spinner.vue → packages/@core/uikit/shadcn-ui/src/components/spinner/spinner.vue

@@ -1,6 +1,4 @@
 <script lang="ts" setup>
-import type { TimeoutHandle } from '@vben/types';
-
 import { ref, watch } from 'vue';
 
 interface Props {
@@ -20,11 +18,12 @@ defineOptions({
 });
 
 const props = withDefaults(defineProps<Props>(), {
-  minLoadingTime: 200,
+  minLoadingTime: 50,
 });
 const startTime = ref(0);
 const showSpinner = ref(false);
-const timer = ref<TimeoutHandle>();
+const renderSpinner = ref(true);
+const timer = ref<ReturnType<typeof setTimeout>>();
 
 watch(
   () => props.spinning,
@@ -39,18 +38,29 @@ watch(
       const loadingTime = performance.now() - startTime.value;
 
       showSpinner.value = loadingTime > props.minLoadingTime;
+      if (showSpinner.value) {
+        renderSpinner.value = true;
+      }
     }, props.minLoadingTime);
   },
   {
     immediate: true,
   },
 );
+
+function onTransitionEnd() {
+  renderSpinner.value = false;
+}
 </script>
 
 <template>
   <div
-    v-if="showSpinner"
-    class="flex-center bg-overlay absolute left-0 top-0 size-full backdrop-blur-sm"
+    v-if="renderSpinner"
+    :class="{
+      'invisible opacity-0': !showSpinner,
+    }"
+    class="flex-center bg-overlay absolute left-0 top-0 size-full backdrop-blur-sm transition-all duration-500"
+    @transitionend="onTransitionEnd"
   >
     <div
       class="loader before:bg-primary/50 after:bg-primary relative h-12 w-12 before:absolute before:left-0 before:top-[60px] before:h-[5px] before:w-12 before:animate-[loader-shadow-ani_0.5s_linear_infinite] before:rounded-[50%] before:content-[''] after:absolute after:left-0 after:top-0 after:h-full after:w-full after:animate-[loader-jump-ani_0.5s_linear_infinite] after:rounded after:content-['']"

+ 17 - 8
packages/business/layouts/src/iframe/iframe-router-view.vue

@@ -1,14 +1,16 @@
 <script lang="ts" setup>
+import type { RouteLocationNormalized } from 'vue-router';
+
 import { computed, ref } from 'vue';
-import { type RouteLocationNormalized, useRoute } from 'vue-router';
+import { useRoute } from 'vue-router';
 
-import { Spinner } from '@vben/common-ui';
 import { preferences } from '@vben-core/preferences';
+import { Spinner } from '@vben-core/shadcn-ui';
 import { useTabsStore } from '@vben-core/stores';
 
 defineOptions({ name: 'IFrameRouterView' });
 
-const spinning = ref(true);
+const spinningList = ref<boolean[]>([]);
 const tabsStore = useTabsStore();
 const route = useRoute();
 
@@ -53,23 +55,30 @@ function canRender(tabItem: RouteLocationNormalized) {
   return tabsStore.getTabs.some((tab) => tab.name === name);
 }
 
-function hideLoading() {
-  spinning.value = false;
+function hideLoading(index: number) {
+  spinningList.value[index] = false;
+}
+
+function showSpinning(index: number) {
+  const curSpinning = spinningList.value[index];
+  // 首次加载时显示loading
+  return curSpinning === undefined ? true : curSpinning;
 }
 </script>
 <template>
   <template v-if="showIframe">
-    <template v-for="item in iframeRoutes" :key="item.fullPath">
+    {{ iframeRoutes.length }}
+    <template v-for="(item, index) in iframeRoutes" :key="item.fullPath">
       <div
         v-show="routeShow(item)"
         v-if="canRender(item)"
         class="relative size-full"
       >
-        <Spinner :spinning="spinning" />
+        <Spinner :spinning="showSpinning(index)" />
         <iframe
           :src="item.meta.iframeSrc as string"
           class="size-full"
-          @load="hideLoading"
+          @load="hideLoading(index)"
         ></iframe>
       </div>
     </template>