video.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. // pages/video/video.js
  2. import {
  3. getVideoList,
  4. getTemplateConfig,
  5. getUuid,
  6. userLogin,
  7. } from "../../api/api";
  8. import {
  9. rnd,
  10. deepCopy,
  11. goToBookDetail,
  12. goToVideoDetail,
  13. SendEvent,
  14. rndone,
  15. } from "../../utils/util";
  16. import { apple } from "../../config/config";
  17. const app = getApp();
  18. let interstitialAd = null;
  19. Page({
  20. data: {
  21. videoList: [],
  22. originVideoList: [],
  23. loading: false,
  24. hasMore: true,
  25. page: 1,
  26. size: 10,
  27. // 状态栏高度
  28. statusBarHeight: 20,
  29. navHeight: 64, // 导航栏高度 = 状态栏 + 44
  30. navStyle: "",
  31. listStyle: "",
  32. // 添加防抖时间戳
  33. lastLoadTime: 0,
  34. ads: null,
  35. globalConfig: null,
  36. // 视频分类列表
  37. categoryList: [],
  38. // 当前选中的分类ID
  39. currentCategoryId: null,
  40. share_count: 1,
  41. hasLoadAdConfig: false,
  42. templateAdId: "",
  43. adErrorField: {
  44. videoError: false,
  45. bannerError: false,
  46. templateError: false,
  47. },
  48. },
  49. onLoad(options) {
  50. console.log("打开视频列表页面--------------------------");
  51. wx.setStorageSync("jump", parseInt(options.jump)); // 是否跳转阅读器
  52. this.autoLogin(options.promotionid);
  53. if (!app.globalData.uuid) {
  54. this.getUUID().then((uuid) => {
  55. // getApp().globalData.uuid = uuid
  56. console.log("登录成功");
  57. });
  58. }
  59. this.handleEntrace(options);
  60. this.setData({
  61. templateAdId: apple.ads.templateAdId,
  62. hasLoadAdConfig: true,
  63. });
  64. this.interstitalLoad();
  65. getTemplateConfig({
  66. box_type: 67,
  67. uuid: app.globalData.uuid,
  68. }).then((res) => {
  69. this.setData({
  70. globalConfig: res.globalConfig,
  71. });
  72. app.globalData.ads_config = res.globalConfig;
  73. // 获取视频分类
  74. this.getVideoCategories();
  75. });
  76. // 获取状态栏高度
  77. this.getSystemInfo();
  78. // this.handleEntrace(options);
  79. },
  80. // 自动登录
  81. async autoLogin(promotionid) {
  82. try {
  83. await getApp().login(promotionid);
  84. } catch (error) {
  85. console.error("登录失败:", error);
  86. }
  87. },
  88. onShow() {
  89. console.log("页面显示--------------------------");
  90. this.rewardVideoLoad();
  91. // 每次显示页面时刷新列表
  92. if (this.data.videoList.length === 0) {
  93. this.setData({
  94. page: 1,
  95. });
  96. }
  97. },
  98. handleEntrace(options) {
  99. console.log("点击入口按钮");
  100. if (!options || Object.keys(options).length === 0) {
  101. return;
  102. }
  103. console.log("入口:", options);
  104. try {
  105. let { type, jump, shared_video_id, tag_id, collectId } = options;
  106. const scene = wx.getStorageSync("scene");
  107. console.log("入口:", type, jump, scene, shared_video_id);
  108. if ((type === "share" || type === "tuiguang") && shared_video_id) {
  109. if (jump === "1") {
  110. console.log("jump为1直接跳转到视频详情页");
  111. // 延迟跳转,确保列表页已经加载完成
  112. setTimeout(() => {
  113. this.goToVideoDetail(shared_video_id, tag_id);
  114. }, 500);
  115. } else {
  116. if (scene === 1007 || scene === 1008) {
  117. console.log("scene为1007或1008且jump不为1,跳转到书详情页");
  118. // 延迟跳转,确保列表页已经加载完成
  119. setTimeout(() => {
  120. this.goToBookDetail(
  121. `&shared_video_id=${shared_video_id}&tag_id=${tag_id}`,
  122. );
  123. }, 500);
  124. } else {
  125. console.log("scene不为1007,1008且jump不为1,跳转到视频详情页");
  126. // 延迟跳转,确保列表页已经加载完成
  127. setTimeout(() => {
  128. this.goToVideoDetail(shared_video_id, tag_id);
  129. }, 500);
  130. }
  131. }
  132. }
  133. if ((type === "share" || type === "tuiguang") && collectId) {
  134. if (scene === 1007 || scene === 1008) {
  135. console.log("scene为1007或1008,跳转到书详情页");
  136. // 延迟跳转,确保列表页已经加载完成
  137. //跳转合集视频
  138. setTimeout(() => {
  139. this.goToBookDetail(`&collectId=${collectId}`);
  140. }, 500);
  141. }
  142. }
  143. } catch (e) {
  144. console.log("处理不同场景进入小程序出错", error);
  145. }
  146. },
  147. onTabItemTap(item) {
  148. console.log(item.index);
  149. setTimeout(() => {
  150. this.interstitalPlayFn();
  151. }, 1000);
  152. },
  153. // 插屏广告加载
  154. interstitalLoad() {
  155. let adId = apple.ads.insertAdId;
  156. interstitialAd = wx.createInterstitialAd({
  157. adUnitId: adId,
  158. });
  159. interstitialAd.onLoad(() => {
  160. console.log("视频列表页插屏广告onLoad");
  161. });
  162. interstitialAd.onError((err) => {
  163. console.log("视频列表页插屏广告onError", err);
  164. });
  165. interstitialAd.onClose(() => {
  166. console.log("视频列表页插屏广告onClose");
  167. });
  168. },
  169. // 插屏广告展示
  170. interstitalPlayFn() {
  171. if (interstitialAd) {
  172. interstitialAd.show().catch((err) => {
  173. console.error("视频列表页插屏显示错误", err);
  174. });
  175. }
  176. },
  177. /**
  178. * 获取uuid
  179. */
  180. getUUID() {
  181. return new Promise((resolve, reject) => {
  182. wx.login({
  183. success: (res) => {
  184. console.log("wxlogin:", res);
  185. let body = {
  186. code: res.code,
  187. source_type: 1,
  188. source_id: "",
  189. };
  190. getUuid(body)
  191. .then(async (uuidres) => {
  192. console.log("登录成功:", uuidres);
  193. app.globalData.uuid = uuidres.uuid;
  194. app.globalData.openId = uuidres.open_id;
  195. // 直接从视频链接进入,同时登录IAA小说用户
  196. // userLogin({
  197. // "appid": apple.appid,
  198. // "channel": 1,
  199. // "srcType": "0",
  200. // "srcAppId": "",
  201. // "srcId": "",
  202. // "cusId": "",
  203. // "inviter": "",
  204. // "promotionId": "",
  205. // "inviterPromotionId": "",
  206. // "system": "pc",
  207. // "type": "inner",
  208. // "inner": 1,
  209. // code: res.code,
  210. // }).then(logres => {
  211. // console.log('userLogin', logres);
  212. // wx.setStorageSync('accessToken', logres.accessToken);
  213. // app.globalData.isLogin = true;
  214. // app.globalData.userCode = logres.userCode;
  215. // app.globalData.openId = logres.openId;
  216. // app.globalData.unionId = logres.unionId;
  217. // console.log('登录成功', app.globalData)
  218. // resolve(logres);
  219. // }).catch(err => {
  220. // app.globalData.isLogin = false;
  221. // reject(err);
  222. // })
  223. console.log("登录后的openid:", uuidres.open_id);
  224. console.log("登录成功:", app.globalData);
  225. SendEvent("login_shanhu", { uuid: uuidres.uuid });
  226. resolve(res.uuid);
  227. })
  228. .catch((err) => {
  229. reject(err);
  230. });
  231. },
  232. });
  233. });
  234. },
  235. // 获取系统信息
  236. getSystemInfo() {
  237. const systemInfo = wx.getSystemInfoSync();
  238. const statusBarHeight = systemInfo.statusBarHeight;
  239. const navHeight = statusBarHeight + 44;
  240. this.setData({
  241. statusBarHeight: statusBarHeight,
  242. navHeight: navHeight,
  243. navStyle: `height:${navHeight}px`,
  244. listStyle: `top:${navHeight}px`,
  245. });
  246. },
  247. // 获取视频分类
  248. getVideoCategories() {
  249. console.log("getVideoCategories", this.data.globalConfig);
  250. // 筛选出type=1的视频分类
  251. const videoCategories = this.data.globalConfig.categoryList.filter(
  252. (item) => item.type === 1,
  253. );
  254. console.log("videoCategories", videoCategories);
  255. if (videoCategories.length > 0) {
  256. // 设置分类列表和默认选中的分类
  257. this.setData({
  258. categoryList: videoCategories,
  259. currentCategoryId: videoCategories[0].id,
  260. });
  261. // 加载第一个分类的视频列表
  262. this.loadVideoList();
  263. } else {
  264. console.error("未找到视频分类");
  265. wx.showToast({
  266. title: "未找到视频分类",
  267. icon: "none",
  268. });
  269. }
  270. },
  271. // 切换分类
  272. switchCategory(e) {
  273. const categoryId = e.currentTarget.dataset.id;
  274. if (categoryId === this.data.currentCategoryId) return;
  275. this.setData({
  276. currentCategoryId: categoryId,
  277. page: 1,
  278. videoList: [],
  279. originVideoList: [],
  280. hasMore: true,
  281. });
  282. this.loadVideoList();
  283. },
  284. // 加载视频列表
  285. async loadVideoList() {
  286. if (this.data.loading) return;
  287. this.setData({
  288. loading: true,
  289. });
  290. try {
  291. const params = {
  292. page: this.data.page,
  293. size: this.data.size,
  294. tag_id: this.data.currentCategoryId,
  295. // 如果不是第一页,传递最后一个视频的ID作为next_id
  296. next_id:
  297. this.data.page > 1 && this.data.videoList.length > 0
  298. ? this.data.videoList[this.data.videoList.length - 1].id
  299. : "",
  300. };
  301. let videoList = [];
  302. try {
  303. videoList = await getVideoList(params);
  304. if (!Array.isArray(videoList)) {
  305. throw new Error("返回数据格式错误");
  306. }
  307. videoList = videoList.map((item) => ({
  308. ...item,
  309. views: rnd(1, 10),
  310. favors: rnd(5000, 10000),
  311. adType: "video",
  312. lock: false,
  313. }));
  314. this.data.originVideoList = this.data.originVideoList.concat(videoList);
  315. this.data.videoList = this.formatVideoAdList(
  316. deepCopy(this.data.originVideoList),
  317. );
  318. } catch (apiError) {
  319. console.error("API调用失败:", apiError);
  320. throw apiError;
  321. }
  322. // 如果是第一页,直接替换数据
  323. if (this.data.page === 1) {
  324. this.setData({
  325. videoList: this.data.videoList,
  326. originVideoList: this.data.originVideoList,
  327. loading: false,
  328. hasMore: videoList.length >= this.data.size,
  329. });
  330. } else {
  331. // 否则追加数据
  332. this.setData({
  333. videoList: this.data.videoList,
  334. originVideoList: this.data.originVideoList,
  335. loading: false,
  336. hasMore: videoList.length >= this.data.size,
  337. });
  338. }
  339. } catch (error) {
  340. console.error("加载视频列表失败:", error);
  341. }
  342. },
  343. /* ------------------ 广告相关 ----------------- */
  344. /**
  345. * 标记广告位置
  346. * @param {} arr
  347. * @returns
  348. */
  349. formatVideoAdList(arr) {
  350. // 原生广告间隔,激励广告间隔
  351. let templateGap = 0,
  352. rewardGap = 0;
  353. console.log(this.data.globalConfig);
  354. if (this.data.globalConfig.isHomeTemplateAd === 1) {
  355. templateGap = this.data.globalConfig.homeTemplateAdInterval;
  356. } else {
  357. templateGap = 0;
  358. }
  359. if (this.data.globalConfig.isHomeRewardAd) {
  360. rewardGap = this.data.globalConfig.homeRewardAdInterval;
  361. } else {
  362. rewardGap = 0;
  363. }
  364. console.log(
  365. "原生间隔templateGap",
  366. templateGap,
  367. "激励间隔rewardGap",
  368. rewardGap,
  369. );
  370. // 第二个固定是原生广告
  371. let result = [
  372. arr.shift(),
  373. {
  374. adType: "templateAd",
  375. },
  376. ];
  377. // 辅助计算间隔变量
  378. let markIndex = 0;
  379. let templateAdFlag = true;
  380. let rewardAdFlag = true;
  381. do {
  382. markIndex += 1;
  383. if (templateGap > 0) {
  384. if (templateAdFlag && markIndex - 1 === templateGap) {
  385. markIndex = 0;
  386. result.push({
  387. adType: "templateAd",
  388. });
  389. templateAdFlag = false;
  390. rewardAdFlag = true;
  391. continue;
  392. }
  393. } else {
  394. rewardAdFlag = true;
  395. }
  396. if (rewardGap > 0) {
  397. if (rewardAdFlag && markIndex - 1 === rewardGap) {
  398. markIndex = 0;
  399. templateAdFlag = true;
  400. rewardAdFlag = false;
  401. let rewardItem = arr.shift();
  402. rewardItem.lock = true;
  403. result.push(rewardItem);
  404. console.log("标记激励项:", rewardItem);
  405. continue;
  406. }
  407. } else {
  408. templateAdFlag = true;
  409. }
  410. result.push(arr.shift());
  411. } while (arr.length > 0);
  412. return result;
  413. },
  414. // 加载更多
  415. loadMore() {
  416. console.log("触发loadMore");
  417. console.log("当前状态:", {
  418. loading: this.data.loading,
  419. hasMore: this.data.hasMore,
  420. page: this.data.page,
  421. videoListLength: this.data.videoList.length,
  422. });
  423. // 添加防抖,避免频繁触发
  424. const now = Date.now();
  425. if (now - this.data.lastLoadTime < 500) {
  426. console.log("防抖触发,跳过本次加载");
  427. return;
  428. }
  429. if (this.data.hasMore && !this.data.loading) {
  430. console.log("开始加载更多数据");
  431. this.setData({
  432. page: this.data.page + 1,
  433. lastLoadTime: now,
  434. });
  435. this.loadVideoList();
  436. } else {
  437. console.log("不满足加载条件:", {
  438. hasMore: this.data.hasMore,
  439. loading: this.data.loading,
  440. });
  441. }
  442. },
  443. // 刷新列表
  444. refreshList() {
  445. wx.showLoading({
  446. title: "刷新中...",
  447. });
  448. this.setData({
  449. page: 1,
  450. videoList: [],
  451. hasMore: true,
  452. });
  453. this.loadVideoList()
  454. .then(() => {
  455. wx.hideLoading();
  456. wx.showToast({
  457. title: "刷新成功",
  458. icon: "success",
  459. duration: 1500,
  460. });
  461. })
  462. .catch(() => {
  463. wx.hideLoading();
  464. });
  465. },
  466. // 点击视频
  467. onVideoTap(e) {
  468. const { id, info, index } = e.currentTarget.dataset;
  469. console.log(id, info, index, "视频详情");
  470. if (!info) {
  471. wx.hideLoading();
  472. wx.showToast({
  473. title: "视频数据错误",
  474. icon: "none",
  475. });
  476. return;
  477. }
  478. SendEvent("click_video_tab_video", {
  479. video_id: id,
  480. tag_id: info.tag_ids[0],
  481. uuid: app.globalData.uuid,
  482. });
  483. const encodedTitle = encodeURIComponent(info.title || "");
  484. this.unlockUrl = `/pages/video/detail?id=${id}&title=${encodedTitle}&tag_id=${info.tag_ids[0]}`;
  485. if (!info.lock) {
  486. // 显示加载中提示
  487. wx.showLoading({
  488. title: "加载中...",
  489. mask: true,
  490. });
  491. // 跳转到视频详情页
  492. wx.navigateTo({
  493. url: this.unlockUrl,
  494. success: () => {
  495. wx.hideLoading();
  496. },
  497. fail: (err) => {
  498. console.error("跳转失败:", err);
  499. wx.hideLoading();
  500. wx.showToast({
  501. title: "跳转失败",
  502. icon: "none",
  503. });
  504. },
  505. });
  506. } else {
  507. this.reawrdVideoIndex = index;
  508. this.rewardVideo = info;
  509. this.rewardVideoPlayFn();
  510. }
  511. },
  512. // 下拉刷新
  513. onPullDownRefresh() {
  514. this.setData({
  515. page: 1,
  516. videoList: [],
  517. });
  518. this.loadVideoList().then(() => {
  519. wx.stopPullDownRefresh();
  520. });
  521. },
  522. // 分享按钮点击事件
  523. onShareTap(e) {
  524. // 阻止事件冒泡
  525. e.stopPropagation();
  526. // SendEvent('click_list_share', {"video_id": video.id, "tag_id": video.tag_ids[0]});
  527. },
  528. // 分享给朋友
  529. onShareAppMessage(e) {
  530. const video = e.target.dataset.video;
  531. if (!video) return {};
  532. const jump = wx.getStorageSync("jump"); // 是否跳转阅读器
  533. let url = "";
  534. url = `/pages/video/video?shared_video_id=${video.id}&title=${encodeURIComponent(video.title)}&tag_id=${video.tag_ids[0]}&share_count=1`;
  535. if (jump === 1) {
  536. url += "&jump=1";
  537. }
  538. SendEvent("click_list_share", {
  539. video_id: video.id,
  540. tag_id: video.tag_ids[0],
  541. uuid: app.globalData.uuid,
  542. });
  543. return {
  544. title: video.title,
  545. path: url,
  546. imageUrl: video.cover_image,
  547. success: function (res) {
  548. wx.showToast({
  549. title: "分享成功",
  550. icon: "success",
  551. duration: 1500,
  552. });
  553. },
  554. fail: function (res) {
  555. wx.showToast({
  556. title: "分享失败",
  557. icon: "none",
  558. duration: 1500,
  559. });
  560. },
  561. };
  562. },
  563. // 分享到朋友圈
  564. onShareTimeline(e) {
  565. const video = e.target.dataset.video;
  566. if (!video) return {};
  567. const jump = wx.getStorageSync("jump"); // 是否跳转阅读器
  568. let url = "";
  569. url = `shared_video_id=${video.id}&title=${encodeURIComponent(video.title)}&tag_id=${video.tag_ids[0]}&share_count=1`;
  570. SendEvent("click_list_share", {
  571. video_id: video.id,
  572. tag_id: video.tag_ids[0],
  573. uuid: app.globalData.uuid,
  574. });
  575. if (jump === 1) {
  576. url += "&jump=1";
  577. }
  578. return {
  579. title: video.title,
  580. query: url,
  581. imageUrl: video.cover_image,
  582. };
  583. },
  584. // 跳转到书籍详情
  585. goToBookDetail(query) {
  586. getApp().globalData.isShowDialog = true;
  587. // 使用公共的goToBookDetail函数
  588. const rndBook = rndone(apple.bookList);
  589. goToBookDetail(
  590. {
  591. bookId: rndBook.bookId,
  592. wxBookId: rndBook.wxBookId,
  593. chapterId: rndBook.chapterId,
  594. },
  595. query,
  596. );
  597. },
  598. // 跳转视频详情
  599. goToVideoDetail(videoId, tagId) {
  600. // 使用公共的goToVideoDetail函数
  601. goToVideoDetail({
  602. videoId: videoId,
  603. tagId: tagId,
  604. });
  605. },
  606. // 激励视频加载
  607. rewardVideoLoad() {
  608. if (wx.createRewardedVideoAd) {
  609. let adId = apple.ads.rewardAdId;
  610. let that = this;
  611. this.videoAd = wx.createRewardedVideoAd({
  612. adUnitId: adId,
  613. });
  614. this.videoAd.onLoad(() => {
  615. console.log("首页激励加载成功");
  616. });
  617. this.videoAd.onError((err) => {
  618. console.log("首页激励失败", err);
  619. });
  620. this.videoAd.onClose((res) => {
  621. // 用户点击了【关闭广告】按钮
  622. this.setData({
  623. isLoadingVideo: false,
  624. });
  625. if (res && res.isEnded) {
  626. // 发放奖励
  627. that.setData({
  628. [`videoList[${that.reawrdVideoIndex}].lock`]: false,
  629. });
  630. console.log("发放奖励跳转");
  631. wx.navigateTo({
  632. url: `${that.unlockUrl}`,
  633. });
  634. }
  635. });
  636. }
  637. },
  638. // 激励视频展示
  639. rewardVideoPlayFn() {
  640. wx.showLoading({
  641. title: "解锁视频中",
  642. });
  643. this.videoAd
  644. .show()
  645. .then(() => {
  646. console.log("激励视频播放成功");
  647. })
  648. .catch((err) => {
  649. console.log("激励视频播放失败", err);
  650. this.setData({
  651. [`videoList[${that.reawrdVideoIndex}].lock`]: false,
  652. });
  653. wx.navigateTo({
  654. url: `${this.unlockUrl}`,
  655. });
  656. })
  657. .then(() => {
  658. wx.hideLoading();
  659. });
  660. },
  661. // 统一小程序广告加载成功
  662. appletsAdLoad(e) {
  663. let _target = e.currentTarget.dataset;
  664. this.setData({
  665. [`adErrorField.${_target.field}`]: false,
  666. });
  667. },
  668. // 统一小程序广告加载失败
  669. appletsAdError(e) {
  670. let _target = e.currentTarget.dataset;
  671. this.setData({
  672. [`adErrorField.${_target.field}`]: true,
  673. });
  674. console.error(_target.name, "错误", e);
  675. },
  676. // 统一小程序广告关闭
  677. appletsAdClose(e) {
  678. let _target = e.currentTarget.dataset;
  679. },
  680. // 统一小程序广告点击
  681. appletsAdClick(e) {
  682. let _target = e.currentTarget.dataset;
  683. },
  684. });