chenlei 1 anno fa
parent
commit
54b30e74a2

+ 1 - 0
components.d.ts

@@ -27,6 +27,7 @@ declare module 'vue' {
     SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
     VanIcon: typeof import('vant/es')['Icon']
     VanImage: typeof import('vant/es')['Image']
+    VanLoading: typeof import('vant/es')['Loading']
     VanSticky: typeof import('vant/es')['Sticky']
     VanSwipe: typeof import('vant/es')['Swipe']
     VanSwipeItem: typeof import('vant/es')['SwipeItem']

+ 16 - 0
src/configure.tsx

@@ -1,6 +1,7 @@
 import { compose, initial } from "@dage/service";
 import { ElMessage } from "element-plus";
 import "element-plus/theme-chalk/el-message.css";
+import router from "./router";
 
 declare global {
   interface DageRequestMeta {
@@ -11,6 +12,11 @@ declare global {
   }
 }
 
+enum RESPONSE_CODE_MAP {
+  NOT_FOUND = 2404,
+  UNDEFINED = 2001,
+}
+
 const showMessage = (msg: string, type = "error") => {
   ElMessage({
     type,
@@ -28,6 +34,16 @@ initial({
     const { showError = true } = request.meta;
 
     if (response.code !== 0 && showError) {
+      if (
+        [RESPONSE_CODE_MAP.NOT_FOUND, RESPONSE_CODE_MAP.UNDEFINED].includes(
+          response.code
+        )
+      ) {
+        router.replace({ name: "404" });
+
+        return response;
+      }
+
       const message = response.__raw__.data.msg ?? "System on business trip";
       showMessage(message);
     }

+ 1 - 1
src/utils/usePagination.ts

@@ -17,7 +17,7 @@ export const usePagination = <T>(
   const loading = ref(false);
   const list = ref<T[]>([]);
   const noData = computed(() => !total.value && !loading.value);
-  const noMore = computed(() => size * pageNum.value < total.value);
+  const noMore = computed(() => size * pageNum.value > total.value);
 
   const getList = async () => {
     try {

+ 1 - 1
src/views/Collections/components/Menu.vue

@@ -46,7 +46,7 @@ const getThumbList = async () => {
   li {
     position: relative;
     border-bottom: 1px solid #fff;
-    background: #181818 no-repeat 100%;
+    background: #181818 no-repeat center / cover;
 
     &:last-child {
       border: none;

+ 85 - 11
src/views/Collections/index.vue

@@ -24,7 +24,11 @@
       <div class="collections-main">
         <Menu />
 
-        <div v-loading="loading" class="collections-main__right">
+        <div
+          v-loading="loading"
+          element-loading-background="rgba(122, 122, 122, 0.8)"
+          class="collections-main__right"
+        >
           <Waterfall :list="list" :gutter="30" :hasAroundGutter="false">
             <template #item="{ item, url }">
               <div
@@ -32,7 +36,12 @@
                 aria-label="Link"
                 @click="handleItemClick(item)"
               >
-                <img alt="" :src="url" aria-label="Image link" />
+                <img
+                  alt=""
+                  :src="url"
+                  aria-label="Image link"
+                  :style="{ height: item.imgHeight + 'px' }"
+                />
                 <div class="collections-item__inner">
                   <p tabindex="0" :aria-description="item.name">
                     {{ item.name }}
@@ -46,6 +55,10 @@
           </Waterfall>
 
           <p v-if="noData" class="no-more">no more</p>
+
+          <div v-if="rendering && pageNum > 1" style="text-align: center">
+            <VanLoading color="var(--van-primary-color)" />
+          </div>
         </div>
       </div>
     </div>
@@ -56,9 +69,8 @@
 
 <script lang="ts" setup>
 import { Waterfall } from "vue-waterfall-plugin-next";
-import type { ViewCard } from "vue-waterfall-plugin-next/dist/types/types/waterfall";
 import { useRoute } from "vue-router";
-import { computed, ref, watch } from "vue";
+import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
 import { getBaseURL } from "@dage/service";
 import { useBaseStore } from "@/stores/base";
 import Breadcrumb from "@/components/Breadcrumb/index.vue";
@@ -83,11 +95,19 @@ const curRoute = computed(() =>
 );
 const visible = ref(false);
 const checkedItemId = ref<number | null>(null);
+// 当前页图片数据是否全部加载完成
+const rendering = ref(false);
+const list = ref<any[]>([]);
+const loading = computed(
+  () => (rendering.value || fetchLoading.value) && pageNum.value === 1
+);
 
 const {
+  pageNum,
   list: sourceList,
   noData,
-  loading,
+  noMore,
+  loading: fetchLoading,
   resetParams,
   getList,
 } = usePagination<CollectionListItem>(
@@ -100,23 +120,77 @@ const {
   PaginationType.DEFAULT,
   20
 );
-const list = computed<any>(() =>
-  sourceList.value.map((i: CollectionListItem) => ({
-    ...i,
-    src: baseUrl + i.thumb,
-  }))
-);
 
 const handleItemClick = (item: any) => {
   checkedItemId.value = item.id;
   visible.value = true;
 };
 
+const getImgRatio: (url: string) => Promise<number> = (url: string) => {
+  return new Promise((res) => {
+    const img = new Image();
+    img.src = url;
+    img.onload = () => {
+      const ratio = Math.round((img.height / img.width) * 100) / 100;
+      res(ratio);
+    };
+    img.onerror = () => {
+      res(0);
+    };
+  });
+};
+
+const handleScroll = () => {
+  // 检查用户是否滚动到页面底部
+  if (
+    window.innerHeight + window.scrollY >= document.body.offsetHeight &&
+    !rendering.value &&
+    !loading.value &&
+    !noMore.value
+  ) {
+    console.log("Scrolled to bottom");
+    pageNum.value += 1;
+    getList();
+  }
+};
+
+onMounted(() => {
+  window.addEventListener("scroll", handleScroll);
+});
+
+onBeforeUnmount(() => {
+  window.removeEventListener("scroll", handleScroll);
+});
+
+watch(sourceList, (v) => {
+  if (!v.length) return;
+
+  rendering.value = true;
+
+  const promises = v.map(async (i) => {
+    const url = baseUrl + i.thumb;
+    const ratio = await getImgRatio(url);
+
+    list.value.push({
+      ...i,
+      imgHeight: 303 * ratio,
+      src: url,
+    });
+
+    return i;
+  });
+
+  Promise.allSettled(promises).then(() => {
+    rendering.value = false;
+  });
+});
+
 watch(
   route,
   () => {
     if (route.name !== "Collections") return;
 
+    list.value = [];
     resetParams();
     getList();
   },

+ 1 - 1
src/views/Exhibitions/Detail/components/Objects.vue

@@ -13,7 +13,7 @@
         <ElImage
           :src="baseUrl + item.filePath"
           fit="cover"
-          style="height: 100%"
+          style="width: 100%; height: 100%"
         />
 
         <div class="exh-detail-object-item__mask">

+ 0 - 7
src/views/Exhibitions/Detail/index.vue

@@ -120,13 +120,6 @@ const getExhibitionDetail = async () => {
     loading.value = true;
     const data = await getExhibitionDetailApi(route.query.id as string);
     detail.value = data;
-  } catch (err) {
-    // @ts-ignore
-    if (err.response.code === 2001) {
-      setTimeout(() => {
-        router.replace({ name: "404" });
-      }, 3000);
-    }
   } finally {
     loading.value = false;
   }

+ 1 - 1
src/views/Exhibitions/index.vue

@@ -92,7 +92,7 @@
         <p v-if="noData" class="no-more">no more</p>
       </div>
 
-      <div v-if="noMore" class="exhibitions__more" @click="handleMore">
+      <div v-if="!noMore" class="exhibitions__more" @click="handleMore">
         Show More
       </div>
     </div>

+ 3 - 5
src/views/LearnEngage/List/index.vue

@@ -1,10 +1,8 @@
 <template>
-  <div style="overflow: hidden">
-    <div v-loading="loading" class="learn-engage-container">
-      <Item v-for="item in list" :key="item.id" :item="item" />
+  <div v-loading="loading" class="learn-engage-container">
+    <Item v-for="item in list" :key="item.id" :item="item" />
 
-      <p v-if="noData" class="no-more">no more</p>
-    </div>
+    <p v-if="noData" class="no-more">no more</p>
   </div>
 
   <div

+ 1 - 0
src/views/Publications/Magazines/index.vue

@@ -39,6 +39,7 @@
           aria-label="Link"
           @mouseenter="
             () => {
+              if (idx === curDateIndex) return;
               curDateIndex = idx;
               debounceSearch();
             }