Bläddra i källkod

优化主页和视频播放器组件,添加主页浏览状态管理,支持从视频播放页返回时恢复状态,提升用户体验和资源管理。

wuyi 2 månader sedan
förälder
incheckning
0120209eef
5 ändrade filer med 188 tillägg och 16 borttagningar
  1. 22 7
      src/components/VideoProcessor.vue
  2. 21 1
      src/router/index.ts
  3. 36 0
      src/store/user.ts
  4. 95 4
      src/views/Home.vue
  5. 14 4
      src/views/VideoPlayer.vue

+ 22 - 7
src/components/VideoProcessor.vue

@@ -578,13 +578,13 @@ const initVideoPlayer = async (): Promise<void> => {
   }
 
   // QQ 浏览器不支持提示
-//   if (isQQBrowser()) {
-//     console.log("检测到 QQ 浏览器,不支持加密视频播放");
-//     error.value =
-//       "QQ浏览器暂不支持播放此类视频\n请使用Chrome、UC或其他浏览器访问";
-//     // 不提供重试选项,直接返回
-//     return;
-//   }
+  //   if (isQQBrowser()) {
+  //     console.log("检测到 QQ 浏览器,不支持加密视频播放");
+  //     error.value =
+  //       "QQ浏览器暂不支持播放此类视频\n请使用Chrome、UC或其他浏览器访问";
+  //     // 不提供重试选项,直接返回
+  //     return;
+  //   }
 
   // UC 浏览器特殊处理 - UC通常支持标准的HLS播放
   if (isUCBrowser()) {
@@ -1146,8 +1146,23 @@ const stopVideo = (): void => {
   if (videoElement.value) {
     videoElement.value.pause();
     videoElement.value.currentTime = 0;
+
+    // 清除视频源
+    videoElement.value.removeAttribute("src");
+    videoElement.value.load();
   }
+
+  // 停止HLS加载
   stopHlsLoading();
+
+  // 清理HLS实例
+  destroyHls();
+
+  // 清理Blob URL
+  if (processedVideoUrl.value?.startsWith("blob:")) {
+    URL.revokeObjectURL(processedVideoUrl.value);
+    processedVideoUrl.value = "";
+  }
 };
 
 // 暴露方法给父组件

+ 21 - 1
src/router/index.ts

@@ -59,6 +59,15 @@ const router = createRouter({
   history: createWebHistory(),
   routes,
   scrollBehavior(to, from, savedPosition) {
+    // 如果是从视频播放页返回到首页,不自动滚动到顶部,让Home.vue组件处理
+    if (
+      from.name === "VideoPlayer" &&
+      to.name === "Home" &&
+      from.meta.returnFromVideo
+    ) {
+      return false; // 不执行自动滚动
+    }
+
     if (savedPosition) {
       return savedPosition;
     } else {
@@ -67,11 +76,22 @@ const router = createRouter({
   },
 });
 
-router.beforeEach((to, _, next) => {
+router.beforeEach((to, from, next) => {
   console.log("Router navigation to:", to.path, to.name);
 
   const userStore = useUserStore();
 
+  // 检查是否从视频页返回到主页
+  if (
+    from.name === "VideoPlayer" &&
+    to.name === "Home" &&
+    from.meta.returnFromVideo
+  ) {
+    console.log("检测到从视频页返回到主页,保留returnFromVideo标记");
+    // 将标记传递给目标路由
+    to.meta.returnFromVideo = true;
+  }
+
   if (to.meta.requiresAuth && !userStore.token) {
     console.log("Redirecting to Home due to auth requirement");
     next({ name: "Home" });

+ 36 - 0
src/store/user.ts

@@ -9,11 +9,34 @@ import {
 import { useStorage } from "@vueuse/core";
 import { VipLevel } from "@/types/vip";
 
+// 主页浏览状态接口定义
+interface HomePageState {
+  selectedMenu: string;
+  currentPage: number;
+  selectedSort: string;
+  scrollPosition: number;
+  isSearchMode: boolean;
+  currentSearchKeyword: string;
+  videoList?: any[];
+  totalPages?: number;
+  totalCount?: number;
+}
+
 export const useUserStore = defineStore("user", () => {
   const token = useStorage("token", "");
   const userInfo = ref<any>({});
   const userManuallyLoggedOut = useStorage("userManuallyLoggedOut", false);
 
+  // 添加主页浏览状态存储
+  const homePageState = ref<HomePageState>({
+    selectedMenu: "",
+    currentPage: 1,
+    selectedSort: "time",
+    scrollPosition: 0,
+    isSearchMode: false,
+    currentSearchKeyword: "",
+  });
+
   const setToken = (newToken: string) => {
     token.value = newToken;
   };
@@ -22,6 +45,16 @@ export const useUserStore = defineStore("user", () => {
     userInfo.value = info;
   };
 
+  // 保存主页浏览状态
+  const saveHomePageState = (state: Partial<HomePageState>) => {
+    homePageState.value = { ...homePageState.value, ...state };
+  };
+
+  // 获取主页浏览状态
+  const getHomePageState = (): HomePageState => {
+    return homePageState.value;
+  };
+
   // 获取用户VIP等级
   const getVipLevel = (): VipLevel => {
     return userInfo.value?.vipLevel || VipLevel.GUEST;
@@ -84,5 +117,8 @@ export const useUserStore = defineStore("user", () => {
     logout,
     sync,
     createGuest,
+    homePageState,
+    saveHomePageState,
+    getHomePageState,
   };
 });

+ 95 - 4
src/views/Home.vue

@@ -364,8 +364,8 @@
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted, onBeforeUnmount } from "vue";
-import { useRouter } from "vue-router";
+import { ref, onMounted, onBeforeUnmount, watch, nextTick } from "vue";
+import { useRouter, useRoute } from "vue-router";
 import { searchVideoByTags, searchVideoByKeyword } from "@/services/api";
 import { videoMenus as fixedVideoMenus } from "@/data/videoMenus";
 import VideoProcessor from "@/components/VideoProcessor.vue";
@@ -374,10 +374,14 @@ import { VipLevel } from "@/types/vip";
 
 // 路由
 const router = useRouter();
+const route = useRoute();
 
 // 用户状态
 const userStore = useUserStore();
 
+// 是否从视频播放页返回
+const isReturnFromVideo = ref(false);
+
 // 生成MAC地址作为设备标识
 const generateMacAddress = (): string => {
   const hex = "0123456789ABCDEF";
@@ -569,6 +573,9 @@ const handleSearch = async (keyword: string, page = 1) => {
 
 // 播放视频
 const playVideo = (video: any) => {
+  // 保存当前浏览状态,以便返回时恢复
+  saveCurrentState();
+
   const vipLevel = userStore.getVipLevel();
 
   if (vipLevel === VipLevel.GUEST || vipLevel === VipLevel.FREE) {
@@ -631,10 +638,83 @@ const loadVideosByTag = async (tagHash: string, page = 1) => {
   }
 };
 
+// 保存当前浏览状态
+const saveCurrentState = () => {
+  // 保存当前滚动位置
+  const scrollPosition = window.scrollY || document.documentElement.scrollTop;
+
+  console.log("保存主页状态,当前视频列表长度:", videoList.value.length);
+
+  userStore.saveHomePageState({
+    selectedMenu: selectedMenu.value,
+    currentPage: currentPage.value,
+    selectedSort: selectedSort.value,
+    scrollPosition: scrollPosition,
+    isSearchMode: isSearchMode.value,
+    currentSearchKeyword: currentSearchKeyword.value,
+    videoList: videoList.value,
+    totalPages: totalPages.value,
+    totalCount: totalCount.value,
+  });
+};
+
+// 恢复保存的浏览状态
+const restoreSavedState = async () => {
+  const savedState = userStore.getHomePageState();
+  console.log("恢复主页状态:", savedState);
+
+  // 恢复状态
+  selectedMenu.value = savedState.selectedMenu;
+  selectedSort.value = savedState.selectedSort;
+  isSearchMode.value = savedState.isSearchMode;
+  currentSearchKeyword.value = savedState.currentSearchKeyword;
+  currentPage.value = savedState.currentPage;
+
+  // 直接使用已保存的视频列表数据,不重新调用API
+  if (savedState.videoList && savedState.videoList.length > 0) {
+    console.log("使用已保存的视频列表数据,不重新调用API");
+    videoList.value = savedState.videoList;
+    totalPages.value = savedState.totalPages || 0;
+    totalCount.value = savedState.totalCount || 0;
+
+    // 使用setTimeout确保DOM完全更新后再滚动
+    setTimeout(() => {
+      console.log("恢复滚动位置:", savedState.scrollPosition);
+      window.scrollTo({
+        top: savedState.scrollPosition,
+        behavior: "auto",
+      });
+    }, 100);
+  } else {
+    console.log("没有保存的视频列表数据,需要重新加载");
+    // 加载视频数据
+    if (isSearchMode.value && currentSearchKeyword.value) {
+      await handleSearch(currentSearchKeyword.value, savedState.currentPage);
+    } else {
+      await loadVideosByTag(selectedMenu.value, savedState.currentPage);
+    }
+
+    // 使用setTimeout确保DOM完全更新后再滚动
+    setTimeout(() => {
+      console.log("恢复滚动位置:", savedState.scrollPosition);
+      window.scrollTo({
+        top: savedState.scrollPosition,
+        behavior: "auto",
+      });
+    }, 100);
+  }
+};
+
 // 初始化视频菜单和加载默认视频
 const initializeVideoMenus = async () => {
-  // 默认不选择任何菜单,加载所有视频
-  await loadVideosByTag("");
+  if (isReturnFromVideo.value) {
+    // 如果是从视频播放页返回,恢复之前的状态
+    await restoreSavedState();
+    isReturnFromVideo.value = false;
+  } else {
+    // 默认不选择任何菜单,加载所有视频
+    await loadVideosByTag("");
+  }
 };
 
 // 监听Header搜索事件
@@ -643,7 +723,18 @@ const handleHeaderSearch = (event: CustomEvent) => {
   handleSearch(keyword);
 };
 
+// 检查是否从视频播放页返回
+const checkReturnFromVideo = () => {
+  // 检查导航来源
+  if (route.meta.returnFromVideo) {
+    isReturnFromVideo.value = true;
+    // 重置路由元数据,避免重复触发
+    route.meta.returnFromVideo = false;
+  }
+};
+
 onMounted(() => {
+  checkReturnFromVideo();
   initializeVideoMenus();
   // 监听Header搜索事件
   window.addEventListener("header-search", handleHeaderSearch as EventListener);

+ 14 - 4
src/views/VideoPlayer.vue

@@ -881,13 +881,23 @@ const formatNumber = (num: string | number): string => {
 
 // 返回主页
 const goBack = () => {
-  // 直接导航到主页
-  router.push("/");
-
-  // 如果需要清理视频播放器资源
+  // 停止视频播放并清除缓存
   if (videoProcessorRef.value) {
     videoProcessorRef.value.stopVideo();
   }
+
+  // 设置路由元数据,标记为从视频页返回
+  // 使用可写的路由元数据对象
+  const returnMeta = {
+    ...router.currentRoute.value.meta,
+    returnFromVideo: true,
+  };
+  router.currentRoute.value.meta = returnMeta;
+
+  console.log("视频页返回,设置returnFromVideo标记", returnMeta);
+
+  // 导航到主页
+  router.push("/");
 };
 
 // 播放视频