details.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. // subpackagestow/details/details.js
  2. // import { models, db, _ } from '../../utils/cloudbase.js'
  3. import { getDB, getModels, getCommand, getTempFileURLs, getClient } from '../../utils/cloudbase.js'
  4. Page({
  5. /**
  6. * 页面的初始数据
  7. */
  8. data: {
  9. fileType: 'video',
  10. viewType: 'grid',
  11. courseList: [],
  12. shouchang: '',
  13. show_1: '',
  14. xiazi: '',
  15. xia: '',
  16. itemlist: {},
  17. isCollected: false,
  18. isPlaying: false,
  19. isAudioPlaying: false,
  20. enterTime: null, // 进入时间
  21. },
  22. async onLoad(options) {
  23. // 进入时间
  24. const now = new Date();
  25. this.setData({
  26. enterTime: now
  27. });
  28. // 获取传递过来的数据
  29. const itemStr = decodeURIComponent(options.item);
  30. const item = JSON.parse(itemStr);
  31. // 格式化时间
  32. item.createdAtText = this.formatTime(item.createdAt);
  33. // 判断 url 字段是否存在且是 cloud://
  34. if (item.url && item.url.length > 0) {
  35. try {
  36. // 只转换 cloud:// 格式的
  37. const cloudUrls = item.url.filter(u => u.startsWith('cloud://'));
  38. if (cloudUrls.length > 0) {
  39. const fileList = await getTempFileURLs(cloudUrls);
  40. // 按顺序替换成 http 链接
  41. fileList.forEach((f, i) => {
  42. if (f.tempFileURL) {
  43. const idx = item.url.indexOf(cloudUrls[i]);
  44. if (idx !== -1) {
  45. item.url[idx] = f.tempFileURL;
  46. }
  47. }
  48. });
  49. }
  50. } catch (err) {
  51. console.error('url 转换失败', err);
  52. }
  53. }
  54. // // 设置到页面数据中
  55. // this.setData({
  56. // itemlist: item
  57. // }, () => {
  58. // // 转换云文件ID为临时链接
  59. // if (item.url && item.url.length > 0) {
  60. // wx.cloud.getTempFileURL({
  61. // fileList: [item.url[0]],
  62. // success: res => {
  63. // if (res.fileList && res.fileList.length > 0) {
  64. // this.setData({
  65. // 'itemlist.playUrl': res.fileList[0].tempFileURL
  66. // });
  67. // }
  68. // },
  69. // fail: err => {
  70. // console.error('获取临时链接失败', err);
  71. // }
  72. // });
  73. // }
  74. this.setData({ itemlist: item })
  75. // 转换云文件ID为临时链接
  76. if (item.url && item.url.length > 0) {
  77. try {
  78. const tempFiles = await getTempFileURLs([item.url[0]])
  79. this.setData({ 'itemlist.playUrl': tempFiles[0].tempFileURL })
  80. } catch (err) {
  81. console.error('获取临时链接失败', err)
  82. }
  83. }
  84. // 相关推荐
  85. this.getcourseList()
  86. // });
  87. // 获取图片
  88. const fileIDs = [
  89. 'cloud://honghgaier-5guiffgcf17a2eea.686f-honghgaier-5guiffgcf17a2eea-1373037829/images/icon/shouchang.png',
  90. 'cloud://honghgaier-5guiffgcf17a2eea.686f-honghgaier-5guiffgcf17a2eea-1373037829/images/icon/show_1.png',
  91. 'cloud://honghgaier-5guiffgcf17a2eea.686f-honghgaier-5guiffgcf17a2eea-1373037829/images/icon/xiazi.png',
  92. 'cloud://honghgaier-5guiffgcf17a2eea.686f-honghgaier-5guiffgcf17a2eea-1373037829/images/icon/xia_1.png'
  93. ];
  94. const fileList = await getTempFileURLs(fileIDs)
  95. this.setData({
  96. shouchang: fileList[0].tempFileURL,
  97. show_1: fileList[1].tempFileURL,
  98. xiazi: fileList[2].tempFileURL,
  99. xia: fileList[3].tempFileURL,
  100. // shouchangs: fileList[4].tempFileURL,
  101. })
  102. // // 并发下载多个 fileID
  103. // Promise.all(
  104. // fileIDs.map(fileID => wx.cloud.downloadFile({ fileID }))
  105. // ).then(results => {
  106. // // 每个 result 对应一个下载结果
  107. // const tempFilePaths = results.map(r => r.tempFilePath);
  108. // console.log('全部下载成功:', tempFilePaths);
  109. // this.setData({
  110. // shouchang: tempFilePaths[0],
  111. // show_1: tempFilePaths[1],
  112. // xiazi: tempFilePaths[2],
  113. // xia: tempFilePaths[3],
  114. // shouchangs: tempFilePaths[4],
  115. // });
  116. // }).catch(err => {
  117. // console.error('有文件下载失败:', err);
  118. // });
  119. },
  120. formatTime(ts) {
  121. const date = new Date(ts);
  122. const y = date.getFullYear();
  123. const m = String(date.getMonth() + 1).padStart(2, '0');
  124. const d = String(date.getDate()).padStart(2, '0');
  125. const hh = String(date.getHours()).padStart(2, '0');
  126. const mm = String(date.getMinutes()).padStart(2, '0');
  127. const ss = String(date.getSeconds()).padStart(2, '0');
  128. return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
  129. },
  130. // 相关课件推荐
  131. async getcourseList() {
  132. console.log(this.data.itemlist._id, this.data.itemlist.range, this.data.itemlist.tag_id, '+++++++++++++++++++++++++++++++++++++++++++++++' );
  133. let client = await getClient(); // 跨小程序 SDK
  134. const result = await client.callFunction({
  135. name: "database", // 云函数部署名
  136. data: {
  137. page: "recommende",
  138. action: "findFileRangee",
  139. data: {
  140. _id: this.data.itemlist._id,
  141. range: this.data.itemlist.range,
  142. tag_id: this.data.itemlist.tag_id,
  143. pageSize: 20,
  144. pageNum: 1,
  145. },
  146. },
  147. });
  148. console.log(result, 'data++++++---------------');
  149. this.setData({
  150. courseList: result.result.records
  151. })
  152. const models = await getModels()
  153. const { data } = await models.file_manage.list({
  154. filter: {
  155. where: {
  156. level: this.data.itemlist.level,
  157. dan: this.data.itemlist.dan,
  158. tag_id: this.data.itemlist.tag_id
  159. }
  160. },
  161. pageSize: 20, // 分页大小,建议指定,如需设置为其它值,需要和 pageNumber 配合使用,两者同时指定才会生效
  162. pageNumber: 1, // 第几页
  163. getCount: true, // 开启用来获取总数
  164. // envType: pre 体验环境, prod 正式环境
  165. envType: "prod",
  166. });
  167. // 返回查询到的数据列表 records 和 总数 total
  168. console.log(data, 'data');
  169. let recordsdata = data.records || []
  170. // 提取 cover 云文件 ID
  171. const coverFileIDs = recordsdata.map(item => item.cover).filter(Boolean)
  172. // 批量获取临时链接
  173. let tempFiles = []
  174. if (coverFileIDs.length > 0) {
  175. try {
  176. tempFiles = await getTempFileURLs(coverFileIDs)
  177. } catch (err) {
  178. console.error('获取课程封面临时链接失败', err)
  179. }
  180. }
  181. // 给每条记录添加可直接渲染的 coverUrl
  182. recordsdata = recordsdata.map((item, index) => ({
  183. ...item,
  184. coverUrl: tempFiles[index] ? tempFiles[index].tempFileURL : ''
  185. }))
  186. this.setData({
  187. courseList: recordsdata
  188. })
  189. },
  190. // 下载
  191. goToGoodsLists(event) {
  192. // 获取绑定的数据
  193. const item = event.currentTarget.dataset.item;
  194. // 将数据转换为 JSON 字符串并传递
  195. const itemStr = encodeURIComponent(JSON.stringify(item));
  196. wx.navigateTo({
  197. url: `/subpackages/down/down?item=${itemStr}`
  198. });
  199. },
  200. // 相关课件点击详情
  201. goToGoodsList(event) {
  202. // 获取绑定的数据
  203. const item = event.currentTarget.dataset.item;
  204. // 将数据转换为 JSON 字符串并传递
  205. const itemStr = encodeURIComponent(JSON.stringify(item));
  206. wx.navigateTo({
  207. url: `/subpackagestow/details/details?item=${itemStr}`
  208. });
  209. },
  210. // // 是否收藏
  211. // async getcollect() {
  212. // const { data } = await models.wx_collect.get({
  213. // filter: {
  214. // where: {
  215. // file_manager_id: this.data.itemlist._id
  216. // }
  217. // },
  218. // // envType: pre 体验环境, prod 正式环境
  219. // envType: "prod",
  220. // });
  221. // // 返回查询到的数据
  222. // console.log(data, '123321');
  223. // // 判断是否有值
  224. // if (data && Object.keys(data).length > 0) {
  225. // this.setData({
  226. // isCollected: true // 设置标志为 true
  227. // });
  228. // } else {
  229. // this.setData({
  230. // isCollected: false // 设置标志为 false
  231. // });
  232. // }
  233. // },
  234. // // 收藏
  235. // async goTocollect() {
  236. // if (this.data.isCollected) {
  237. // const { data } = await models.wx_collect.delete({
  238. // filter: {
  239. // where: {
  240. // file_manager_id: _.eq(this.data.itemlist._id), // 收藏文件id
  241. // }
  242. // },
  243. // // envType: pre 体验环境, prod 正式环境
  244. // envType: "prod",
  245. // });
  246. // // 返回删除成功的条数
  247. // console.log(data, '删除');
  248. // wx.showToast({ title: '取消收藏成功', icon: 'success' });
  249. // this.setData({
  250. // isCollected: false
  251. // })
  252. // } else {
  253. // const userInfo = wx.getStorageSync('userInfo');
  254. // const { data } = await models.wx_collect.create({
  255. // data: {
  256. // wx_user_id: userInfo._id, // 收藏人_id
  257. // file_manager_id: this.data.itemlist._id, // 收藏文件id
  258. // remark: "备注备注备注备注备注备注", // 备注
  259. // },
  260. // // envType: pre 体验环境, prod 正式环境
  261. // envType: "prod",
  262. // });
  263. // // 返回创建的数据 id
  264. // console.log(data);
  265. // wx.showToast({ title: '收藏成功', icon: 'success' });
  266. // this.setData({
  267. // isCollected: true // 设置标志为 true
  268. // });
  269. // }
  270. // },
  271. // 预览
  272. async previewPDF() {
  273. const fileUrl = this.data.itemlist.url[0];
  274. console.log(fileUrl, 'this.data.itemlist.url[0]');
  275. // 提取文件后缀
  276. const extension = fileUrl.substring(fileUrl.lastIndexOf('.') + 1).toLowerCase();
  277. // 文档类文件需要下载
  278. if (['pdf', 'ppt', 'pptx', 'xls', 'xlsx'].includes(extension)) {
  279. try {
  280. let downloadUrl = fileUrl;
  281. if (fileUrl.startsWith('cloud://')) {
  282. // cloud:// 文件获取临时链接
  283. const fileList = await getTempFileURLs([fileUrl]);
  284. if (!fileList || !fileList[0]?.tempFileURL) throw new Error('获取临时链接失败');
  285. downloadUrl = fileList[0].tempFileURL;
  286. }
  287. // 下载到本地临时文件
  288. const res = await new Promise((resolve, reject) => {
  289. wx.downloadFile({
  290. url: downloadUrl,
  291. success: resolve,
  292. fail: reject
  293. });
  294. });
  295. // 打开文档
  296. wx.openDocument({
  297. filePath: res.tempFilePath,
  298. fileType: extension,
  299. success: () => console.log('文档打开成功'),
  300. fail: err => console.error('文档打开失败', err)
  301. });
  302. } catch (err) {
  303. console.error('文档下载失败', err);
  304. wx.showToast({ title: '文档下载失败', icon: 'none' });
  305. }
  306. } else if (['mp3', 'aac', 'wav'].includes(extension)) {
  307. console.log('音频文件,直接播放');
  308. this.toggleAudio(); // HTTP 链接直接播放
  309. } else if (['mp4', 'mov'].includes(extension)) {
  310. console.log('视频文件,直接播放');
  311. this.toggleVideo(); // HTTP 链接直接播放
  312. } else {
  313. wx.showToast({ title: '暂不支持该文件类型预览', icon: 'none' });
  314. }
  315. // const client = await getClient(); // ✅ 获取跨小程序云实例
  316. // console.log(this.data.itemlist.url[0], 'this.data.itemlist.url[0]');
  317. // client.downloadFile({
  318. // fileID: this.data.itemlist.url[0],
  319. // success: res => {
  320. // const filePath = res.tempFilePath;
  321. // const extension = filePath.substring(filePath.lastIndexOf('.') + 1).toLowerCase();
  322. // console.log('文件后缀:', extension);
  323. // // 根据文件类型处理
  324. // if (['pdf', 'ppt', 'pptx'].includes(extension)) {
  325. // wx.openDocument({
  326. // filePath: filePath,
  327. // fileType: extension,
  328. // success: () => {
  329. // console.log('文档打开成功');
  330. // },
  331. // fail: err => {
  332. // console.error('文档打开失败', err);
  333. // }
  334. // });
  335. // } else if (['mp3', 'aac', 'wav'].includes(extension)) {
  336. // console.log('这是一个音频文件,可以跳转到播放页面或使用音频组件');
  337. // this.toggleAudio()
  338. // } else if (['mp4', 'mov'].includes(extension)) {
  339. // console.log('这是一个视频文件,可以跳转到视频播放页');
  340. // this.toggleVideo()
  341. // } else {
  342. // wx.showToast({
  343. // title: '暂不支持该文件类型预览',
  344. // icon: 'none'
  345. // });
  346. // }
  347. // },
  348. // fail: err => {
  349. // console.error('文件下载失败', err);
  350. // }
  351. // });
  352. },
  353. // 视频播放还是暂停
  354. toggleVideo() {
  355. const videoContext = wx.createVideoContext('myVideo', this);
  356. if (this.data.isPlaying) {
  357. videoContext.pause();
  358. this.setData({ isPlaying: false });
  359. } else {
  360. videoContext.play();
  361. this.setData({ isPlaying: true });
  362. }
  363. },
  364. // 音频的播放暂停
  365. async toggleAudio() {
  366. const fileUrl = this.data.itemlist.url[0];
  367. // 如果已经有临时播放链接,直接用它
  368. if (this.data.itemlist.playUrl) {
  369. this._playAudio(this.data.itemlist.playUrl);
  370. } else if (fileUrl.startsWith('cloud://')) {
  371. // // 否则判断 url 是否是 cloud:// 开头,需要转临时链接
  372. // const cloudUrl = this.data.itemlist.url[0];
  373. // if (cloudUrl && cloudUrl.startsWith('cloud://')) {
  374. // const fileList = await getTempFileURLs([cloudUrl]);
  375. // if (fileList && fileList.length > 0) {
  376. // const tempUrl = fileList[0].tempFileURL;
  377. // this.setData({ 'itemlist.playUrl': tempUrl }, () => {
  378. // this._playAudio(tempUrl);
  379. // });
  380. // } else {
  381. // wx.showToast({ title: '获取播放链接失败', icon: 'none' });
  382. // }
  383. // } else {
  384. // // 不是 cloud:// 开头,直接播放原链接
  385. // this._playAudio(cloudUrl);
  386. // }
  387. // cloud:// 走临时链接逻辑
  388. try {
  389. const fileList = await getTempFileURLs([fileUrl]);
  390. if (fileList.length > 0) {
  391. const tempUrl = fileList[0].tempFileURL;
  392. this.setData({ 'itemlist.playUrl': tempUrl }, () => this._playAudio(tempUrl));
  393. } else {
  394. wx.showToast({ title: '获取播放链接失败', icon: 'none' });
  395. }
  396. } catch (err) {
  397. wx.showToast({ title: '获取播放链接失败', icon: 'none' });
  398. console.error(err);
  399. }
  400. } else {
  401. // HTTP 链接直接播放
  402. this._playAudio(fileUrl);
  403. }
  404. },
  405. _playAudio(url) {
  406. if (!this.audioContext) {
  407. this.audioContext = wx.createInnerAudioContext();
  408. this.audioContext.onPlay(() => this.setData({ isAudioPlaying: true }));
  409. this.audioContext.onPause(() => this.setData({ isAudioPlaying: false }));
  410. this.audioContext.onStop(() => this.setData({ isAudioPlaying: false }));
  411. this.audioContext.onEnded(() => this.setData({ isAudioPlaying: false }));
  412. this.audioContext.onError((res) => {
  413. wx.showToast({ title: '音频播放错误', icon: 'none' });
  414. this.setData({ isAudioPlaying: false });
  415. console.error('音频播放错误', res);
  416. });
  417. }
  418. if (this.data.isAudioPlaying) {
  419. this.audioContext.pause();
  420. } else {
  421. this.audioContext.src = url;
  422. this.audioContext.play();
  423. }
  424. },
  425. onUnload() {
  426. if (this.audioContext) {
  427. this.audioContext.stop();
  428. this.audioContext.destroy();
  429. this.audioContext = null;
  430. }
  431. },
  432. async onUnload() {
  433. await this.saveUserBehavior();
  434. if (this.audioContext) {
  435. this.audioContext.stop();
  436. this.audioContext.destroy();
  437. this.audioContext = null;
  438. }
  439. },
  440. async onHide() {
  441. await this.saveUserBehavior();
  442. if (this.audioContext) {
  443. this.audioContext.pause();
  444. }
  445. },
  446. async saveUserBehavior() {
  447. try {
  448. const userInfo = wx.getStorageSync('userInfo');
  449. const userId = userInfo && userInfo._id ? userInfo._id : '';
  450. const startTime = this.data.enterTime;
  451. if (!startTime) return; // 如果没进入过,不存
  452. const endTime = new Date();
  453. // 转换为时间戳(毫秒 → 秒)
  454. const startTimestamp = new Date(startTime).getTime();
  455. const endTimestamp = new Date().getTime();
  456. const duration = endTimestamp - startTimestamp;
  457. const models = await getModels()
  458. await models.monitor_behavior.create({
  459. data: {
  460. start_time: startTimestamp, // 数字
  461. end_time: endTimestamp, // 数字
  462. duration: duration, // 数字
  463. type: 1, // 固定 0
  464. user_id: userId || 'guest'
  465. },
  466. envType: "prod",
  467. });
  468. console.log("监控数据保存成功:", {
  469. start_time: startTimestamp,
  470. end_time: endTimestamp,
  471. duration,
  472. type: 0,
  473. user_id: userId || 'guest'
  474. });
  475. } catch (err) {
  476. console.error("监控数据保存失败", err);
  477. }
  478. }
  479. })