Selaa lähdekoodia

Merge branch 'feat/视频合集' into v1.7

# Conflicts:
#	api/api.js
#	app.js
#	config/config.js
erekook 11 kuukautta sitten
vanhempi
commit
b346b6195c

+ 48 - 12
api/api.js

@@ -7,6 +7,8 @@ import {
   uploadTrackLog,
   bookPlatformPost,
 } from "../utils/request";
+import { https, apple } from "../config/config.js";
+
 export const userLogin = (data) => requestLogin(data);
 
 // 获取token
@@ -142,7 +144,7 @@ export function getVideoList(params = {}) {
   };
 
   return postWithCommonParams(
-    "https://applet.xiaoduer.cn/cashvideoapi/cashvideo/getvideolist",
+    `${https.goHttps}/cashvideoapi/cashvideo/getvideolist`,
     requestData,
   ).then((data) => {
     return data;
@@ -166,7 +168,7 @@ export function getVideoDetail(params = {}) {
   }
 
   return postWithCommonParams(
-    "https://applet.xiaoduer.cn/cashvideoapi/cashvideo/getvideoinfo",
+    `${https.goHttps}/cashvideoapi/cashvideo/getvideoinfo`,
     params,
   );
 }
@@ -177,7 +179,7 @@ export function getRealVideoUrl(id, tagId) {
     return Promise.reject(new Error("缺少必要参数: id 或 tagId"));
   }
 
-  const url = `https://applet.xiaoduer.cn/coral/cashvideo/getOasVideoUrl?id=${id}&tagId=${tagId}`;
+  const url = `${https.goHttps}/coral/cashvideo/getOasVideoUrl?id=${id}&tagId=${tagId}`;
   return postWithCommonParams(url, {}, "GET");
 }
 
@@ -194,7 +196,7 @@ export function getRelatedVideos(params = {}) {
   };
 
   return post(
-    "https://applet.xiaoduer.cn/cashvideoapi/cashvideo/getrandvideolistpro",
+    `${https.goHttps}/cashvideoapi/cashvideo/getrandvideolistpro`,
     requestData,
   );
 }
@@ -203,18 +205,12 @@ export function getRelatedVideos(params = {}) {
  * 获取 uuid
  */
 export function getUuid(body) {
-  return postWithCommonParams(
-    "https://applet.xiaoduer.cn/api/user/getuuid",
-    body,
-  );
+  return postWithCommonParams(`${https.goHttps}/api/user/getuuid`, body);
 }
 
 // https://applet.xiaoduer.cn/coral/template/config
 export function getTemplateConfig(body) {
-  return postWithCommonParams(
-    "https://applet.xiaoduer.cn/coral/template/config",
-    body,
-  );
+  return postWithCommonParams(`${https.goHttps}/coral/template/config`, body);
 }
 
 // 导出postWithCommonParams
@@ -228,3 +224,43 @@ export const recordLog = (body) => uploadTrackLog("/track", body);
 // 上报腾讯广告
 export const userActive = (params) =>
   bookPlatformPost("/api/inner/user/active", params);
+
+// 获取合集视频
+export function getCollectDetail(collectionId) {
+  const requestData = {
+    collectionId,
+    uuid: getApp().globalData.uuid, // 固定值,实际应用中可能需要动态生成
+    gh_id: apple.ghid, // 固定值
+  };
+
+  return postWithCommonParams(
+    `${https.goHttps}/materialvideoapi/materialvideo/getCollectionVideoList`,
+    requestData,
+  ).then((data) => {
+    return data;
+  });
+}
+
+// 获取真实视频播放地址
+export function getCollectRealVideoUrl(url) {
+  return postWithCommonParams(url, {}, "GET");
+}
+
+// 获取推荐合集列表
+export function getRecCollectList(params) {
+  const { tagIdList, count, id } = params;
+  const requestData = {
+    id,
+    tagIdList,
+    count,
+    uuid: getApp().globalData.uuid, // 固定值,实际应用中可能需要动态生成
+    gh_id: apple.ghid, // 固定值
+  };
+
+  return postWithCommonParams(
+    `${https.goHttps}/materialvideoapi/materialvideo/getRandomCollectionList`,
+    requestData,
+  ).then((data) => {
+    return data;
+  });
+}

+ 1 - 2
app.js

@@ -1,5 +1,4 @@
 import { SendEvent } from "/utils/util.js";
-
 import { unlockChapter, userActive } from "/api/api.js";
 
 // app.js
@@ -128,7 +127,7 @@ App({
     userInfo: null,
     isLogin: false,
     novelManager: {},
-    uuid: "",
+    uuid: "fuck",
     isShowDialog: false,
     novelManagerId: 0,
     novelId: "",

+ 4 - 2
app.json

@@ -9,7 +9,8 @@
     "pages/book/list",
     "pages/search/search",
     "pages/video/video",
-    "pages/video/detail"
+    "pages/video/detail",
+    "pages/collect-video/video"
   ],
   "window": {
     "navigationStyle": "custom",
@@ -67,4 +68,5 @@
   "componentFramework": "glass-easel",
   "sitemapLocation": "sitemap.json",
   "lazyCodeLoading": "requiredComponents"
-}
+}
+

BIN
assets/images/playing.png


BIN
assets/images/tv.png


+ 2 - 0
config/config.js

@@ -554,4 +554,6 @@ export const https = {
   dev_apiHttps: "https://hyperion-novel-qa-app.mokamrp.com",
   test_apiHttps: "https://hyperion-novel-qa-app.mokamrp.com",
   apiHttps: "https://hyperion-novel-qa-app.mokamrp.com",
+  goHttps: "https://applet.xiaoduer.cn",
+  // goHttps: "http://172.16.18.51:8080",
 };

+ 597 - 0
pages/collect-video/video.js

@@ -0,0 +1,597 @@
+// pages/collect-video/video.js
+import {
+  getCollectDetail,
+  getTemplateConfig,
+  getUuid,
+  getCollectRealVideoUrl,
+  getRecCollectList,
+} from "../../api/api";
+import { rnd, SendEvent, rndone } from "../../utils/util";
+import { apple } from "../../config/config";
+
+const app = getApp();
+let interstitialAd = null;
+
+Page({
+  data: {
+    loading: false,
+    hasMore: true,
+    // 状态栏高度
+    statusBarHeight: 20,
+    navHeight: 64, // 导航栏高度 = 状态栏 + 44
+    navStyle: "",
+    listStyle: "",
+    ads: null,
+    globalConfig: null,
+
+    hasLoadAdConfig: false,
+    templateAdId: "",
+    adErrorField: {
+      videoError: false,
+      bannerError: false,
+      templateError: false,
+    },
+
+    collectId: "", // 合集id
+    tagId: "", // 合集标签id
+    eps: [], // 合集视频列表
+    playingEp: 0, // 正在播放集
+    videoDetail: null,
+
+    isPlaying: false,
+    videoStatus: 1,
+    duration: 0, // 视频总时长
+    currentTime: 0, // 当前播放时间
+  },
+
+  onLoad(options) {
+    console.log("打开视频合集页面--------------------------");
+    // wx.setStorageSync("jump", parseInt(options.jump)); // 是否跳转阅读器
+    this.autoLogin(options.promotionid);
+    if (!app.globalData.uuid) {
+      this.getUUID().then((uuid) => {
+        // getApp().globalData.uuid = uuid
+        console.log("登录成功");
+      });
+    }
+
+    this.handleEntrace(options);
+    this.setData({
+      collectId: options.collectId,
+      templateAdId: apple.ads.templateAdId,
+      hasLoadAdConfig: true,
+    });
+
+    getTemplateConfig({
+      box_type: 73,
+      uuid: app.globalData.uuid,
+    }).then((res) => {
+      this.setData({
+        globalConfig: res.globalConfig,
+      });
+      this.interstitalLoad();
+      this.rewardVideoLoad();
+      setTimeout(() => {
+        this.interstitalPlayFn();
+      }, 5000);
+
+      // 获取合集视频详情
+      this.getCollectVideoDetail();
+      this.loadVideoList();
+    });
+
+    // 获取状态栏高度
+    this.getSystemInfo();
+    // this.handleEntrace(options);
+  },
+
+  // 自动登录
+  async autoLogin(promotionid) {
+    try {
+      await getApp().login(promotionid);
+    } catch (error) {
+      console.error("登录失败:", error);
+    }
+  },
+
+  onShow() {},
+
+  handleEntrace(options) {
+    // console.log("点击入口按钮");
+    // if (!options || Object.keys(options).length === 0) {
+    //   return;
+    // }
+    // console.log("入口:", options);
+    // try {
+    // } catch (e) {
+    //   console.log("处理不同场景进入小程序出错", error);
+    // }
+  },
+
+  clickBack() {
+    wx.switchTab({
+      url: "/pages/video/video",
+    });
+  },
+
+  // 获取视频合集详情
+  getCollectVideoDetail() {
+    // 显示加载中提示
+    wx.showLoading({
+      title: "加载中...",
+      mask: true,
+    });
+    getCollectDetail(this.data.collectId)
+      .then((res) => {
+        if (res && res.video.length > 0) {
+          res.video
+            .sort((a, b) => a.sort < b.sort)
+            .map((e, i) => {
+              // -------- 锁定判断 --------
+              if (this.isCollectNeedLock()) {
+                // set lock status
+                if (i + 1 >= res.lockStart) {
+                  e.lock = true;
+                } else {
+                  e.lock = false;
+                }
+              } else {
+                e.lock = false;
+              }
+              return e;
+            });
+          this.setData({
+            eps: res.video,
+            tagId: res.tagId,
+            playingEp: 0,
+            videoDetail: res.video[0],
+          });
+          // 默认播放第一集
+          this.getVideoPlayUrl();
+        }
+      })
+      .catch((err) => {
+        console.error(err);
+      })
+      .then(() => {
+        wx.hideLoading();
+      });
+  },
+
+  // 判断合集是否需要锁定
+  isCollectNeedLock() {
+    // 获取当前日期(格式:YYYY-MM-DD)
+    const today = new Date().toISOString().split("T")[0];
+
+    // 从 localStorage 获取数据
+    const hadRewardNum = parseInt(
+      wx.getStorageSync("had_reward_num") || "0",
+      10,
+    );
+    const unlockCollectIds = JSON.parse(
+      wx.getStorageSync("unlock_collect_ids") || "[]",
+    );
+    const lastCheckDate = wx.getStorageSync("last_check_date");
+
+    if (this.data.globalConfig.isShowReward === 1) {
+      // 检查是否需要重置(新的一天)
+      if (lastCheckDate !== today) {
+        // 重置解锁数据
+        wx.setStorageSync("had_reward_num", "0");
+        wx.setStorageSync("unlock_collect_ids", "[]");
+        wx.setStorageSync("last_check_date", today);
+        return true; // 新的一天,当前视频未解锁
+      }
+
+      // 判断解锁状态
+      // 1. 如果当天广告观看次数 >= 3,所有视频解锁
+      if (hadRewardNum >= 3) {
+        return false;
+      }
+
+      if (unlockCollectIds.includes(this.data.collectId)) {
+        return false;
+      }
+      return true;
+    } else {
+      return false;
+    }
+  },
+
+  async getVideoPlayUrl() {
+    try {
+      const res = await getCollectRealVideoUrl(this.data.videoDetail.url);
+      this.setData({
+        "videoDetail.playUrl": res.url_info[0].url,
+      });
+      setTimeout(() => {
+        const videoContext = wx.createVideoContext("my-video");
+        videoContext.play();
+      }, 200);
+      // 获取视频实例并播放
+    } catch (error) {}
+  },
+
+  // 点击集
+  clickEp(e) {
+    const { ep, idx } = e.currentTarget.dataset;
+    if (idx === this.data.playingEp) return;
+    this.setData({
+      playingEp: idx,
+      videoDetail: ep,
+    });
+    SendEvent("click_collection_video", {
+      cid: this.data.collectId,
+      video_id: this.data.eps[this.data.playingEp].id,
+      tag_id: this.data.tagId,
+    });
+    if (!this.data.videoDetail.lock) {
+      this.getVideoPlayUrl();
+    }
+  },
+
+  clickUnlock() {
+    this.rewardVideoPlayFn();
+  },
+  /**
+   * 获取uuid
+   */
+  getUUID() {
+    return new Promise((resolve, reject) => {
+      wx.login({
+        success: (res) => {
+          console.log("wxlogin:", res);
+          let body = {
+            code: res.code,
+            source_type: 1,
+            source_id: "",
+          };
+          getUuid(body)
+            .then(async (uuidres) => {
+              console.log("登录成功:", uuidres);
+              app.globalData.uuid = uuidres.uuid;
+              app.globalData.openId = uuidres.open_id;
+              console.log("登录后的openid:", uuidres.open_id);
+              console.log("登录成功:", app.globalData);
+              SendEvent("login_shanhu", { uuid: uuidres.uuid });
+              resolve(res.uuid);
+            })
+            .catch((err) => {
+              reject(err);
+            });
+        },
+      });
+    });
+  },
+
+  // 获取系统信息
+  getSystemInfo() {
+    const systemInfo = wx.getSystemInfoSync();
+    const statusBarHeight = systemInfo.statusBarHeight;
+    const navHeight = statusBarHeight + 44;
+
+    this.setData({
+      statusBarHeight: statusBarHeight,
+      navHeight: navHeight,
+      navStyle: `height:${navHeight}px`,
+      listStyle: `top:${navHeight}px`,
+    });
+  },
+
+  // 加载视频列表
+  async loadVideoList() {
+    this.setData({
+      loading: true,
+    });
+
+    try {
+      const params = {
+        tagIdList: this.data.globalConfig.tagIds.join(","),
+        count: this.data.globalConfig.recommendListNum,
+        id: Number(this.data.collectId),
+      };
+      const res = await getRecCollectList(params);
+      res.forEach((e) => {
+        e.adType = "video";
+        e.views = rnd(1, 10);
+      });
+      this.setData({
+        videoList: res,
+        loading: false,
+        hasMore: false,
+      });
+    } catch (error) {
+      console.error("加载视频列表失败:", error);
+    }
+  },
+
+  // 视频播放
+  makeVideoPlay() {
+    if (!this.data.videoDetail.lock && this.data.videoStatus == 2) {
+      const videoContext = wx.createVideoContext("my-video");
+      videoContext.play();
+    }
+  },
+
+  // 视频暂停
+  makeVideoPause() {
+    if (this.data.videoStatus == 1) {
+      const videoContext = wx.createVideoContext("my-video");
+      videoContext.pause();
+    }
+  },
+
+  // 视频播放事件
+  onVideoPlay() {
+    this.setData({
+      isPlaying: true,
+      videoStatus: 1,
+    });
+
+    // 可以添加播放埋点
+    SendEvent("collection_video_play", {
+      cid: this.data.collectId,
+      video_id: this.data.eps[this.data.playingEp].id,
+      tag_id: this.data.tagId,
+    });
+  },
+
+  // 视频暂停事件
+  onVideoPause() {
+    this.setData({
+      isPlaying: false,
+      videoStatus: 2,
+    });
+  },
+
+  // 视频播放结束事件
+  onVideoEnd() {
+    this.setData({
+      isPlaying: false,
+      videoStatus: 0,
+    });
+
+    const nextEp = this.data.playingEp + 1;
+    // 最后一集播放完,随机播放下一个合集
+    if (nextEp === this.data.eps.length && this.data.videoList.length > 0) {
+      const rndCollect = rndone(this.data.videoList);
+      this.setData({
+        collectId: rndCollect.id,
+      });
+      this.getCollectVideoDetail();
+    } else {
+      // 切换下一集
+      if (nextEp <= this.data.eps.length - 1) {
+        this.setData({
+          playingEp: nextEp,
+          videoDetail: this.data.eps[nextEp],
+        });
+        if (!this.data.videoDetail.lock) {
+          this.getVideoPlayUrl();
+        }
+      }
+    }
+  },
+
+  // 视频时间更新事件
+  onVideoUpdate(e) {
+    const { currentTime, duration } = e.detail;
+    this.setData({
+      currentTime,
+      duration,
+    });
+  },
+
+  // 视频元数据加载完成
+  onVideoLoaded(e) {
+    console.log("视频元数据加载完成", e.detail);
+  },
+
+  /* ------------------ 广告相关 ----------------- */
+  /**
+   * 标记广告位置
+   * @param {} arr
+   * @returns
+   */
+  formatVideoAdList(arr) {
+    // 原生广告间隔,激励广告间隔
+    let templateGap = 0,
+      rewardGap = 0;
+    console.log(this.data.globalConfig);
+    if (this.data.globalConfig.isHomeTemplateAd === 1) {
+      templateGap = this.data.globalConfig.homeTemplateAdInterval;
+    } else {
+      templateGap = 0;
+    }
+    if (this.data.globalConfig.isHomeRewardAd) {
+      rewardGap = this.data.globalConfig.homeRewardAdInterval;
+    } else {
+      rewardGap = 0;
+    }
+    console.log(
+      "原生间隔templateGap",
+      templateGap,
+      "激励间隔rewardGap",
+      rewardGap,
+    );
+
+    // 第二个固定是原生广告
+    let result = [
+      arr.shift(),
+      {
+        adType: "templateAd",
+      },
+    ];
+    // 辅助计算间隔变量
+    let markIndex = 0;
+    let templateAdFlag = true;
+    let rewardAdFlag = true;
+
+    do {
+      markIndex += 1;
+      if (templateGap > 0) {
+        if (templateAdFlag && markIndex - 1 === templateGap) {
+          markIndex = 0;
+          result.push({
+            adType: "templateAd",
+          });
+          templateAdFlag = false;
+          rewardAdFlag = true;
+          continue;
+        }
+      } else {
+        rewardAdFlag = true;
+      }
+      if (rewardGap > 0) {
+        if (rewardAdFlag && markIndex - 1 === rewardGap) {
+          markIndex = 0;
+          templateAdFlag = true;
+          rewardAdFlag = false;
+          let rewardItem = arr.shift();
+          rewardItem.lock = true;
+          result.push(rewardItem);
+          console.log("标记激励项:", rewardItem);
+          continue;
+        }
+      } else {
+        templateAdFlag = true;
+      }
+
+      result.push(arr.shift());
+    } while (arr.length > 0);
+
+    return result;
+  },
+
+  // 点击合集
+  clickCollect(e) {
+    const { id, info, index } = e.currentTarget.dataset;
+    this.setData({
+      collectId: id,
+      tagId: info.tagId,
+    });
+    SendEvent("click_collection", {
+      cid: this.data.collectId,
+      tag_id: this.data.tagId,
+    });
+    console.log(id, info, index, "合集详情");
+    this.getCollectVideoDetail();
+  },
+
+  // 分享给朋友
+  onShareAppMessage(e) {},
+
+  // 激励视频加载
+  rewardVideoLoad() {
+    if (this.data.globalConfig.isShowReward !== 1) return;
+    if (wx.createRewardedVideoAd) {
+      let adId = apple.ads.rewardAdId;
+      this.videoAd = wx.createRewardedVideoAd({
+        adUnitId: adId,
+      });
+
+      this.videoAd.onLoad(() => {
+        console.log("首页激励加载成功");
+      });
+      this.videoAd.onError((err) => {
+        console.log("首页激励失败", err);
+      });
+      this.videoAd.onClose((res) => {
+        // 用户点击了【关闭广告】按钮
+        if (res && res.isEnded) {
+          // 发放奖励
+          const hadRewardNum = parseInt(
+            wx.getStorageSync("had_reward_num") || "0",
+            10,
+          );
+          const unlockCollectIds = JSON.parse(
+            wx.getStorageSync("unlock_collect_ids") || "[]",
+          );
+          wx.setStorageSync("had_reward_num", hadRewardNum + 1);
+          wx.setStorageSync(
+            "unlock_collect_ids",
+            JSON.stringify([...unlockCollectIds, this.data.collectId]),
+          );
+          // 解锁当前合集,播放视频
+          this.data.eps = this.data.eps.map((e) => {
+            e.lock = false;
+            return e;
+          });
+          this.setData({
+            "videoDetail.lock": false,
+            eps: this.data.eps,
+          });
+
+          SendEvent("unlock_collection", {
+            cid: this.data.collectId,
+            video_id: this.data.eps[this.playingEp].id,
+            // tag_id: this.data.tagId,
+            ad_num: hadRewardNum + 1,
+          });
+
+          this.getVideoPlayUrl();
+        } else {
+          SendEvent("unlock_collection_fail", {
+            cid: this.data.collectId,
+            video_id: this.data.eps[this.playingEp].id,
+          });
+        }
+      });
+    }
+  },
+
+  // 激励视频展示
+  rewardVideoPlayFn() {
+    if (this.data.globalConfig.isShowReward !== 1) return;
+    wx.showLoading({
+      title: "解锁视频中",
+    });
+    this.videoAd
+      .show()
+      .then(() => {
+        console.log("激励视频播放成功");
+      })
+      .catch((err) => {
+        console.log("激励视频播放失败", err);
+      })
+      .then(() => {
+        wx.hideLoading();
+      });
+  },
+
+  // 插屏广告加载
+  interstitalLoad() {
+    if (this.data.globalConfig.isShowInsert !== 1) return;
+    let adId = apple.ads.insertAdId;
+    interstitialAd = wx.createInterstitialAd({
+      adUnitId: adId,
+    });
+    interstitialAd.onLoad(() => {
+      console.log("合集视频页插屏广告onLoad");
+    });
+    interstitialAd.onError((err) => {
+      console.log("合集视频页插屏广告onError", err);
+    });
+    interstitialAd.onClose(() => {
+      console.log("合集视频页插屏广告onClose");
+      this.makeVideoPlay();
+    });
+  },
+
+  // 插屏广告展示
+  interstitalPlayFn() {
+    if (this.data.globalConfig.isShowInsert !== 1) return;
+    if (interstitialAd) {
+      interstitialAd
+        .show()
+        .then(() => {
+          setTimeout(() => {
+            this.makeVideoPause();
+          }, 500);
+        })
+        .catch((err) => {
+          console.error("合集视频页插屏显示错误", err);
+        });
+    }
+  },
+});

+ 4 - 0
pages/collect-video/video.json

@@ -0,0 +1,4 @@
+{
+  "backgroundTextStyle": "dark",
+  "usingComponents": {}
+}

+ 119 - 0
pages/collect-video/video.wxml

@@ -0,0 +1,119 @@
+<!--pages/video/video.wxml-->
+<view class="nav-bar" style="{{navStyle}}">
+  <image class="icon-back" src="/assets/images/icons/icon-back-black.png" style="top:{{statusBarHeight}}px" bindtap="clickBack">
+</image>
+</view>
+<view class="collect-video-container">
+  <view class="video-main">
+    <block wx:if="{{eps && eps.length > 0}}">
+      <video autoplay="{{true}}" src="{{videoDetail.playUrl}}" direction="{{0}}"
+        bindplay="onVideoPlay" bindpause="onVideoPause" bindended="onVideoEnd" class="my-video" id="my-video"
+        style="height: 100%" bindtimeupdate="onVideoUpdate" bindseekcomplete="onVideoSeek"
+        bindloadedmetadata="onVideoLoaded" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portrait">
+        <view wx:if="{{isShowComplainBtn}}" class="complaint-btn" catchtap="handleComplain">投诉</view>
+        <!-- 激励使用设置的首帧图 -->
+        <!-- <image wx:if="{{videoDetail.coverUrl && !videoDetail.lock}}" class="poster" src="{{videoDetail.coverUrl}}"> -->
+        <!-- </image> -->
+        <view catchtap="handleClickRewardMask" class="reward-mask" wx:if="{{videoDetail.lock}}">
+          <view class="reward-btn" catchtap="clickUnlock">
+            <image class="icon-lock" src="/assets/images/lock.png"></image>
+            <text>看完广告解锁本合集所有内容</text>
+          </view>
+          <view class="reward-tip">今日看满3个广告可解锁本小程序所有内容</view>
+        </view>
+      </video>
+    </block>
+    
+    <!-- 加载中提示 -->
+    <view wx:if="{{(isLoading || !videoDetail.playUrl) && !videoDetail.lock}}" class="loading-container">
+      <view class="loading-spinner"></view>
+      <view class="loading-text">{{isLoading ? '加载中...' : '获取视频中...'}}</view>
+    </view>
+  </view>
+  <view class="video-info">
+    <view class="video-title">{{videoDetail.videoTitle}}</view>
+    <view class="video-desc">
+      <text class="xuanji">选集</text>
+      <text class="total-ep">共{{eps.length}}集</text>
+      <text class="ep-tip">锁定集在观看广告后可解锁</text>
+    </view>
+    <scroll-view scroll-x class="video-eps-scroll">
+      <view class="video-eps">
+        <view wx:for="{{eps}}" class="ep-item" data-ep="{{item}}" data-idx="{{index}}" bindtap="clickEp">
+        <view class="ep-item-title">
+            <image wx:if="{{playingEp === index && !item.lock}}" class="icon" src="/assets/images/playing.png"></image>
+            <image wx:if="{{item.lock}}" class="icon" src="/assets/images/lock.png"></image>
+            <text style="color: {{playingEp === index ? '#CA4D7A':'#ffffff'}}">第{{index + 1}}集</text>
+          </view>
+          <image class="ep-item-cover" src="{{item.coverUrl}}"></image>
+      </view>
+      </view>
+    </scroll-view>
+  </view>
+  <view class="title">猜你喜欢</view>
+  <!-- 视频列表 -->
+  <scroll-view
+    scroll-y
+    class="list-container"
+    enhanced="{{ true }}"
+    show-scrollbar="{{ false }}"
+    bounces="{{ true }}"
+    fast-deceleration="{{ true }}"
+  >
+    <view class="list">
+      <block wx:for="{{ videoList }}" wx:key="id" wx:for-index="idx">
+        <!-- 视频广告 -->
+        <!-- <view wx:if="{{item.adType === 'videoAd' && hasLoadAdConfig}}" class="ad-custom-card"> -->
+        <!--   <ad unit-id="{{templateAdId}}" ad-type="video" style="height: 100%;margin-bottom: 20rpx;"></ad> -->
+        <!-- </view> -->
+        <view wx:if="{{ item.adType === 'video' }}">
+          <!-- 视频卡片 -->
+          <view class="video-item" bindtap="clickCollect" data-info="{{ item }}" data-id="{{ item.id }}" data-index="{{idx}}">
+            <view class="video-cover-container">
+              <image
+                class="video-cover"
+                src="{{ item.coverUrl }}"
+                mode="aspectFill"
+              />
+            </view>
+            <view class="video-item-right">
+              <view class="video-title">{{ item.title }}</view>
+              <view class="video-play-count">
+                <image class="icon" src="/assets/images/tv.png"></image>
+                <text>{{ item.views }}w+人观看</text>
+              </view>
+            </view>
+          </view>
+        </view>
+         <!-- 卡片原生广告 -->
+         <!-- <view wx:if="{{item.adType === 'templateAd' && hasLoadAdConfig }}" class="ad-custom-card-1"> -->
+         <!--    <ad-custom unit-id="{{templateAdId}}" style="height: 100%;margin-bottom: 20rpx;"></ad-custom> -->
+         <!--  </view> -->
+      </block>
+    </view>
+
+    <!-- 加载状态 -->
+    <view class="loading-status">
+      <!-- loading 提示 -->
+      <view class="loading" wx:if="{{ loading }}">
+        <view class="loading-spinner"></view>
+        <text>加载中...</text>
+      </view>
+
+      <!-- 没有更多 -->
+      <view
+        class="finished"
+        wx:if="{{ !loading && videoList.length > 0 && !hasMore }}"
+      >
+        <text>没有更多视频了</text>
+      </view>
+
+      <!-- 空状态 -->
+      <view class="empty" wx:if="{{ !loading && videoList.length === 0 }}">
+        <image class="empty-icon" src="/assets/images/empty.png"></image>
+        <text>暂无视频内容</text>
+      </view>
+    </view>
+  </scroll-view>
+
+</view>

+ 299 - 0
pages/collect-video/video.wxss

@@ -0,0 +1,299 @@
+/* pages/collect-video/video/video.wxss */
+.nav-bar {
+  background-color: #fff;
+  position: relative;
+}
+
+.icon-back {
+  width: 50rpx;
+  height: 50rpx;
+  position: absolute;
+  left: 15px;
+}
+.collect-video-container {
+  height: 100vh;
+  background-color: #f8f8f8;
+  position: relative;
+}
+
+.video-main {
+  position: relative;
+  width: 100%;
+  height: 500rpx;
+}
+
+.my-video {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
+.reward-mask {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  background-color: #333;
+}
+
+.reward-btn {
+  background-color: #1b8edb;
+  width: 460rpx;
+  height: 80rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #ffffff;
+  font-size: 26rpx;
+  border-radius: 8rpx;
+  margin-bottom: 20rpx;
+}
+
+.reward-btn .icon-lock {
+  width: 40rpx;
+  height: 40rpx;
+  margin-right: 10rpx;
+}
+
+.reward-tip {
+  color: #ffffff;
+  font-size: 24rpx;
+  padding-top: 20rpx;
+}
+
+/* 加载中样式 */
+.loading-container {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  background-color: #000;
+}
+
+.loading-spinner {
+  width: 80rpx;
+  height: 80rpx;
+  border: 6rpx solid rgba(255, 255, 255, 0.1);
+  border-left-color: #fff;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+.loading-text {
+  margin-top: 20rpx;
+  font-size: 28rpx;
+  color: rgba(255, 255, 255, 0.7);
+}
+
+.video-info {
+  padding: 30rpx;
+}
+
+.video-info .video-title {
+  width: 100%;
+  font-size: 34rpx;
+  font-family:
+    PingFang SC,
+    PingFang SC-Bold;
+  letter-spacing: 2rpx;
+  font-weight: bold;
+  overflow: hidden;
+  white-space: wrap;
+}
+
+.video-desc {
+  margin-top: 30rpx;
+  margin-bottom: 30rpx;
+}
+
+.video-desc .xuanji {
+  color: #666;
+  font-size: 30rpx;
+  margin-right: 20rpx;
+}
+
+.video-desc .total-ep {
+  color: #999;
+  font-size: 30rpx;
+  margin-right: 20rpx;
+}
+
+.video-desc .ep-tip {
+  color: #bbb;
+  font-size: 30rpx;
+}
+
+.video-eps-scroll {
+  width: 100%;
+}
+
+.video-eps {
+  display: flex;
+  flex-wrap: nowrap;
+}
+
+.ep-item {
+  flex-shrink: 0;
+  width: 200rpx;
+  height: 140rpx;
+  border-radius: 8rpx;
+  position: relative;
+  margin-right: 15rpx;
+}
+
+.ep-item .ep-item-title {
+  width: 200rpx;
+  height: 50rpx;
+  position: absolute;
+  top: 0;
+  left: 0;
+  color: #fff;
+  background-color: rgba(0, 0, 0, 0.7);
+  border-top-left-radius: 8rpx;
+  border-top-right-radius: 8rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 26rpx;
+}
+
+.ep-item .ep-item-title .icon {
+  width: 30rpx;
+  height: 30rpx;
+  margin-right: 15rpx;
+}
+
+.ep-item .ep-item-cover {
+  width: 100%;
+  height: 100%;
+  border-radius: 8rpx;
+}
+
+.title {
+  padding-left: 30rpx;
+  font-size: 34rpx;
+  color: #111;
+}
+
+/* 列表容器 */
+.list-container {
+  height: calc(100vh - 500rpx);
+  background-color: #f8f8f8;
+}
+
+.list {
+  padding: 30rpx;
+  box-sizing: border-box;
+}
+
+/* 视频卡片样式 */
+.video-item {
+  position: relative;
+  margin-bottom: 20rpx;
+  display: flex;
+}
+
+.video-cover-container {
+  width: 240rpx;
+  height: 160rpx;
+  padding-right: 20rpx;
+}
+
+.video-cover-container .video-cover {
+  width: 100%;
+  height: 100%;
+  border-radius: 8rpx;
+}
+
+.video-item-right {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.video-item-right .video-title {
+  font-size: 34rpx;
+  color: #333;
+}
+.video-item-right .video-play-count .icon {
+  width: 30rpx;
+  height: 30rpx;
+  margin-right: 20rpx;
+}
+
+.video-item-right .video-play-count {
+  font-size: 28rpx;
+  color: #bfbfbf;
+  display: flex;
+  align-items: center;
+  margin-top: 20rpx;
+}
+
+/* 加载状态样式 */
+.loading-status {
+  padding: 30rpx 0;
+  text-align: center;
+  font-size: 28rpx;
+  color: #999;
+}
+
+.loading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100rpx;
+}
+
+.loading-spinner {
+  width: 40rpx;
+  height: 40rpx;
+  border: 4rpx solid #f3f3f3;
+  border-top: 4rpx solid #07c160;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
+  margin-bottom: 10rpx;
+}
+
+.finished {
+  height: 80rpx;
+  line-height: 80rpx;
+  color: #999;
+}
+
+.empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 100rpx 0;
+}
+
+.empty-icon {
+  width: 200rpx;
+  height: 200rpx;
+  margin-bottom: 20rpx;
+}
+
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}

+ 9 - 2
project.private.config.json

@@ -24,11 +24,18 @@
     "miniprogram": {
       "list": [
         {
+          "name": "合集视频",
+          "pathName": "pages/collect-video/video",
+          "query": "collectId=4",
+          "scene": null,
+          "launchMode": "default"
+        },
+        {
           "name": "测试",
           "pathName": "pages/video/video",
           "query": "shared_video_id=413559&tag_id=2072&share_count=1&type=tuiguang",
-          "scene": 1008,
-          "launchMode": "default"
+          "launchMode": "default",
+          "scene": 1008
         },
         {
           "name": "非1007,1008场景进入播放",