Ver Fonte

feat: 实现消息详情页滚动加载

jack há 5 meses atrás
pai
commit
0b17655dd0

+ 20 - 3
src/components/NotificationMessageCard/src/NotificationMessageCard.vue

@@ -4,7 +4,7 @@ import PasswordCard from './components/PasswordCard.vue'
 import FeatureUpdateCard from './components/FeatureUpdateCard.vue'
 import { useNotificationMessage } from '@/stores/modules/notificationMessage'
 import { cloneDeep } from 'lodash'
-import { DynamicScroller, DynamicScrollerItem } from 'vue3-virtual-scroller'
+// import { DynamicScroller, DynamicScrollerItem } from 'vue3-virtual-scroller'
 import 'vue3-virtual-scroller/dist/vue3-virtual-scroller.css'
 import { formatTimezone } from '@/utils/tools'
 
@@ -203,6 +203,7 @@ const scrollParentBoxStyle = computed(() => {
 const scrollContainerRef = ref<HTMLElement | null>(null)
 const loading = ref(false)
 const finished = ref(false)
+const prevScrollTop = ref(0)
 
 const loadMore = async () => {
   if (loading.value || finished.value) return
@@ -214,16 +215,29 @@ const onScroll = () => {
   const el = scrollContainerRef.value
   if (!el || loading.value || finished.value) return
 
+  prevScrollTop.value = el.scrollTop
+
   const threshold = 50 // 提前50px触发
   if (el.scrollHeight - el.scrollTop - el.clientHeight <= threshold) {
     loadMore()
   }
 }
 
+const sentinel = ref<HTMLElement | null>(null)
+
+const adjustScrollTop = () => {
+  const el = scrollContainerRef.value
+  if (el) {
+    // 如果滚动容器存在,调整其 scrollTop
+    el.scrollTop = prevScrollTop.value
+  }
+}
+
 defineExpose({
   loading,
   finished,
   scrollContainerRef,
+  adjustScrollTop,
   setAllMessageRead
 })
 </script>
@@ -260,10 +274,13 @@ defineExpose({
         </div>
       </div>
     </template>
-    <div class="footer">
+    <div
+      class="footer"
+      v-if="pageData?.[0]?.notificationType !== 'feature' && pageData?.length > 0"
+    >
       <el-divider v-if="loading"> loading... </el-divider>
       <el-divider v-if="finished && pageData.length > 0">
-        Only display the message data within one year
+        Only display the message data within three months
       </el-divider>
     </div>
   </div>

+ 33 - 5
src/views/Layout/src/components/Header/components/NotificationDrawer.vue

@@ -18,8 +18,34 @@ const notificationTypeList = ref({
   Feature_Update: 'Feature Update'
 })
 
+// const notificationContentRef = ref<HTMLElement | null>(null)
+// const finished = ref(false)
+// const prevScrollTop = ref(0)
+// const scrollContainerRef = ref()
+// const onScroll = () => {
+//   const el = scrollContainerRef.value
+//   if (!el || loading.value || finished.value) return
+
+//   prevScrollTop.value = el.scrollTop
+
+//   const threshold = 50 // 提前50px触发
+//   if (
+//     el.scrollHeight - el.scrollTop - el.clientHeight <= threshold &&
+//     (loading.value || finished.value)
+//   ) {
+//     loading.value = true
+//     getNotificationList()
+//   }
+// }
+// onMounted(() => {
+//   scrollContainerRef.value = notificationContentRef.value?.querySelector('.scroller') as HTMLElement
+//   const el = scrollContainerRef.value
+//   if (!el) return
+//   el.addEventListener('scroll', onScroll)
+// })
+
 const notificationList = ref<any[]>([])
-const notificationMessageCardsRef = ref()
+const notificationMessageCardRef = ref()
 const pageInfo = ref({
   cp: 1,
   ps: 5
@@ -36,7 +62,7 @@ const getNotificationList = () => {
       if (res.code === 200) {
         notificationList.value = [...notificationList.value, ...res.data]
         if (res.data.length === 0 || res.data.length < pageInfo.value.ps) {
-          notificationMessageCardsRef.value.finished = true
+          notificationMessageCardRef.value.finished = true
         }
         // nextTick(() => {
         //   init()
@@ -46,7 +72,9 @@ const getNotificationList = () => {
     .finally(() => {
       loading.value = false
       pageInfo.value.cp += 1
-      notificationMessageCardsRef.value.loading = false
+      notificationMessageCardRef.value.loading = false
+
+      notificationMessageCardRef.value.adjustScrollTop()
     })
 }
 
@@ -56,7 +84,7 @@ const handleMarkAllRead = () => {
     $api.setMessageRead({ read_type: true }).then((res) => {
       if (res.code === 200) {
         notificationMsgStore.hasUnreadMessages()
-        notificationMessageCardsRef.value.setAllMessageRead()
+        notificationMessageCardRef.value.setAllMessageRead()
       }
     })
   } catch (error) {
@@ -144,7 +172,7 @@ const closeDrawer = () => {
             @loading="getNotificationList"
             :data="notificationList"
             :topOffset="185"
-            ref="notificationMessageCardsRef"
+            ref="notificationMessageCardRef"
           />
         </div>
       </el-scrollbar>

+ 33 - 56
src/views/SystemMessage/src/SystemMessage.vue

@@ -71,17 +71,12 @@ const setActiveItem = (item: string) => {
 const loading = ref(false)
 const notificationList = ref<any[]>([])
 
-// const unreadNotificationList = computed(() => {
-//   return notificationList.value.filter((item) => !item.info.isRead)
-// })
 const unreadNotificationList = ref<any[]>([])
-// const readNotificationList = computed(() => {
-//   return notificationList.value.filter((item) => item.info.isRead)
-// })
+
 const readNotificationList = ref<any[]>([])
 const pageInfo = ref({
   cp: 0,
-  ps: 5
+  ps: 7
 })
 const getNotificationList = async (
   tabType: string = 'All Notifications',
@@ -94,6 +89,11 @@ const getNotificationList = async (
     unreadNotificationList.value = []
     readNotificationList.value = []
     notificationList.value = []
+    if (activeCardTypeName.value === 'Feature Update') {
+      pageInfo.value.ps = 100
+    } else {
+      pageInfo.value.ps = 7
+    }
   } else {
     pageInfo.value.cp += 1
   }
@@ -108,6 +108,7 @@ const getNotificationList = async (
   } else {
     info_type = false
   }
+
   try {
     await notificationMsgStore.markMessageAsRead()
     $api
@@ -118,61 +119,38 @@ const getNotificationList = async (
         info_type
       })
       .then((res) => {
-        // if (res.code === 200) {
-        //   const data = res.data
-        //   if (!data?.cardList?.length || data?.cardList?.length < pageInfo.value.ps) {
-        //     notificationMessageCardRef.value.finished = true
-        //   }
-
-        //   if (tabType === 'All Notifications') {
-        //     tabCountList.value = data.countList
-        //     if (isChangeNav) {
-        //       notificationList.value = [...data.cardList]
-        //     } else {
-        //       notificationList.value = [...notificationList.value, ...data.cardList]
-        //     }
-        //   } else if (tabType === 'Unread') {
-        //     if (isChangeNav) {
-        //       unreadNotificationList.value = [...data.cardList]
-        //     } else {
-        //       unreadNotificationList.value = [...unreadNotificationList.value, ...data.cardList]
-        //     }
-        //   } else {
-        //     if (isChangeNav) {
-        //       readNotificationList.value = [...data.cardList]
-        //     } else {
-        //       readNotificationList.value = [...readNotificationList.value, ...data.cardList]
-        //     }
-        //   }
-        // }
-        const data = res.data
+        if (res.code === 200) {
+          const data = res.data
 
-        // 判断是否结束加载
-        if (!data?.cardList?.length || data.cardList.length < pageInfo.value.ps) {
-          notificationMessageCardRef.value.finished = true
-        }
+          // 判断是否结束加载
+          if (!data?.cardList?.length || data.cardList.length < pageInfo.value.ps) {
+            notificationMessageCardRef.value.finished = true
+          }
 
-        const lists = {
-          'All Notifications': { list: notificationList, count: tabCountList },
-          Unread: { list: unreadNotificationList },
-          Read: { list: readNotificationList }
-        }
+          const lists = {
+            'All Notifications': { list: notificationList, count: tabCountList },
+            Unread: { list: unreadNotificationList },
+            Read: { list: readNotificationList }
+          }
 
-        const config = lists[tabType]
-        if (config) {
-          config.list.value = isChangeNav
-            ? [...data.cardList]
-            : [...config.list.value, ...data.cardList]
-        }
+          const config = lists[tabType]
+          if (config) {
+            config.list.value = isChangeNav
+              ? [...data.cardList]
+              : [...config.list.value, ...data.cardList]
+          }
 
-        if (config?.count) {
-          config.count.value = data.countList
+          if (config?.count) {
+            config.count.value = data.countList
+          }
         }
       })
       .finally(() => {
         loading.value = false
         curTabCount.value = []
         notificationMessageCardRef.value.loading = false
+
+        notificationMessageCardRef.value.adjustScrollTop()
       })
   } catch (error) {
     loading.value = false
@@ -277,11 +255,10 @@ onMounted(() => {
               <div
                 class="count"
                 :style="{ 'padding-top': isMac ? 0 : '1px' }"
-                v-if="unreadNotificationList.length > 0 || unreadCardCount > 0"
+                v-if="unreadNotificationList.length > 0 && unreadCardCount > 0"
               >
-                <span>{{
-                  handleCount(unreadNotificationList.length) || handleCount(unreadCardCount)
-                }}</span>
+                <!-- handleCount(unreadNotificationList.length) || -->
+                <span>{{ handleCount(unreadCardCount) }}</span>
               </div>
             </template>
             <div style="padding-bottom: 20px" v-if="activeTabName === 'Unread'">

+ 80 - 8
src/views/SystemMessage/src/components/SystemMessageDetail.vue

@@ -15,24 +15,72 @@ const notificationData = ref({
 })
 
 const loading = ref(false)
+const finished = ref(false)
 if (route.query.type === 'feature') {
   loading.value = true
 }
+const pageInfo = ref({
+  cp: 0,
+  ps: 6
+})
+const scrollContainerRef = ref<HTMLElement | null>(null)
+const isScrollLoading = ref(false)
+const prevScrollTop = ref(0)
+const adjustScrollTop = () => {
+  const el = scrollContainerRef.value
+  if (el) {
+    // 如果滚动容器存在,调整其 scrollTop
+    el.scrollTop = prevScrollTop.value
+  }
+}
+const onScroll = async () => {
+  const el = scrollContainerRef.value
+  if (!el || isScrollLoading.value || finished.value) return
+
+  prevScrollTop.value = el.scrollTop
+
+  const threshold = 50 // 提前50px触发
+  console.log('onScroll', el.scrollHeight, el.scrollTop, el.clientHeight, threshold)
+  if (el.scrollHeight - el.scrollTop - el.clientHeight <= threshold) {
+    loading.value = true
+    isScrollLoading.value = true
+    await getNotificationList()
+  }
+}
+
 const getNotificationList = async () => {
+  pageInfo.value.cp += 1
   await $api
     .getNotificationDetails({
       rules_type: route.query.rules_type,
       frequency_type: route.query.frequency_type,
-      insert_date_format: route.query.insert_date_format
+      insert_date_format: route.query.insert_date_format,
+      cp: pageInfo.value.cp,
+      ps: pageInfo.value.ps
     })
     .then((res) => {
       if (res.code === 200) {
-        notificationData.value = res.data
+        const data = res.data
+        if (data?.length === 0 || !data) {
+          finished.value = true
+          return
+        }
+        const messageList = notificationData.value.notificationList || []
+        // 合并新数据和旧数据
+        notificationData.value = data
+        notificationData.value.notificationList = [...messageList, ...data.notificationList]
         notificationMsgStore.hasUnreadMessages()
+        // 判断是否结束加载
+        if (!data.notificationList?.length || data.notificationList?.length < pageInfo.value.ps) {
+          finished.value = true
+        }
+        scrollContainerRef.value.scrollTop = prevScrollTop.value
       }
     })
     .finally(() => {
+      console.log('getNotificationList finished')
       loading.value = false
+      isScrollLoading.value = false
     })
 }
 
@@ -72,7 +120,7 @@ const handleIframeLoaded = () => {
 
 <template>
   <div class="system-message-detail" v-vloading="loading">
-    <div class="content" v-if="route.query.type !== 'feature'">
+    <div class="content message-list-box" v-if="route.query.type !== 'feature'">
       <div class="header" v-if="notificationData.title">
         <div class="status-icon"></div>
         <div class="title">{{ notificationData.title }}</div>
@@ -100,11 +148,26 @@ const handleIframeLoaded = () => {
           </span>
         </div>
       </div>
-      <EventCard
-        v-for="(item, index) in notificationData.notificationList"
-        :key="index"
-        :data="item"
-      />
+      <div
+        style="height: calc(100% - 70px); overflow: auto"
+        ref="scrollContainerRef"
+        @scroll="onScroll"
+      >
+        <EventCard
+          v-for="(item, index) in notificationData.notificationList"
+          :key="index"
+          :data="item"
+        />
+        <div class="footer">
+          <el-divider style="margin-bottom: 0px" v-if="isScrollLoading"> loading... </el-divider>
+          <el-divider
+            style="margin-bottom: 0px"
+            v-if="finished && notificationData.notificationList.length > 0"
+          >
+            Only display the message data within three months
+          </el-divider>
+        </div>
+      </div>
     </div>
     <div class="content" style="height: 100%" v-else>
       <div class="header" v-if="notificationData.title">
@@ -133,6 +196,11 @@ const handleIframeLoaded = () => {
     margin: auto;
     width: 1000px;
   }
+  .message-list-box {
+    height: 100%;
+    padding: 16px;
+    background-color: var(--color-dialog-body-bg);
+  }
   .notification-card {
     max-width: 800px;
   }
@@ -158,6 +226,10 @@ const handleIframeLoaded = () => {
     line-height: 40px;
     font-size: 12px;
   }
+  .footer {
+    padding-right: 16px;
+    text-align: center;
+  }
   .feature-pdf {
     height: calc(100% - 28px);
   }