courseItem.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. <template>
  2. <view class="courselist-item" >
  3. <view class="courselist-con x-start" @click="toCourseDetail(info)">
  4. <view class="courselist-img">
  5. <!-- <view class="status">进行中</view> -->
  6. <image :src="info.thumbnail" mode="aspectFill"></image>
  7. </view>
  8. <view class="courselist-con-r">
  9. <view @click.passive.stop>
  10. <text class="more-t ">{{info.title}}</text>
  11. <view class="btn_icon" style="margin-left: 5px;" @click="copyId">ID
  12. <image src="https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/images/copy_icon.png" mode="aspectFill"></image>
  13. </view>
  14. </view>
  15. <view class="courselist-desc one-t" v-show="from != 'course'">{{info.courseName}}</view>
  16. <view :class="from == 'course' ? 'courselist-con-timebox ':'courselist-con-timebox x-f'">
  17. <view class="x-f acea-row"><u-icon class="icon" name="file-text" color="#999"
  18. size="20"></u-icon>{{info.createTime}}</view>
  19. <view class="x-f acea-row"><u-icon class="icon" name="clock" color="#999"
  20. size="16"></u-icon>{{$formatSeconds(info.duration,1)}}</view>
  21. </view>
  22. </view>
  23. </view>
  24. <view class="courselist-footer x-f ">
  25. <!--#ifdef MP-WEIXIN-->
  26. <view class="courselist-footer-item x-c " @click="handleClick()"><u-icon
  27. name="share-square" color="#1677ff" size="18"></u-icon>分享课程</view>
  28. <!--#endif-->
  29. <!--#ifdef H5-->
  30. <view class="courselist-footer-item x-c " @click="handleShare"><u-icon
  31. name="share-square" color="#1677ff" size="18"></u-icon>分享课程</view>
  32. <!--#endif-->
  33. <view class="courselist-footer-item x-c shishi" v-show="activeTab == 1" @click="handleStatistics"><u-icon
  34. name="share-square" color="#1677ff" size="18"></u-icon>课程统计</view>
  35. <view class="courselist-footer-item x-c shuju" v-show="activeTab == 2" @click="handleStatistics"><u-icon
  36. name="share-square" color="#1677ff" size="18"></u-icon>数据统计</view>
  37. </view>
  38. <!-- 分享弹窗 -->
  39. <u-popup :show="showShare" :closeOnClickOverlay="true" :round='20' @close="closeShare" @open="openShare">
  40. <view class="sharePop x-ac">
  41. <!--#ifdef MP-WEIXIN-->
  42. <view class="sharePop-item y-f card-share" >
  43. <image src="https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/images/card_icon.png" mode="aspectFill"
  44. style="width: 80rpx; height: 80rpx;margin-top: 20rpx;"></image>
  45. <view style="font-weight: bold;margin-bottom: 4px;">生成卡片</view>
  46. <view style="font-size: 12px;color: #888;">指导分享轻松转发</view>
  47. </view>
  48. <!--#endif-->
  49. <!--#ifdef H5-->
  50. <view class="sharePop-item y-f" @click="buildimg">
  51. <image src="https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/images/poster_icon.png" mode="aspectFill"></image>
  52. <view style="font-weight: bold;margin-bottom: 4px;">生成海报</view>
  53. <view style="font-size: 12px;color: #888;">保存海报美观宣传</view>
  54. </view>
  55. <view class="sharePop-item y-f" @click="copyLink">
  56. <image src="https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/images/link_icon.png" mode="aspectFill"></image>
  57. <view style="font-weight: bold;margin-bottom: 4px;">复制链接</view>
  58. <view style="font-size: 12px;color: #888;">生成链接一键复制</view>
  59. </view>
  60. <!--#endif-->
  61. </view>
  62. </u-popup>
  63. <!-- 设置链接有效时长弹窗 -->
  64. <u-modal :show="setTimeShow" content='content' class="model" @confirm="confirmTime" :closeOnClickOverlay='true'
  65. @close="closetext">
  66. <view class="setTimebox">
  67. <view class="timetip">不传默认以系统参数为准</view>
  68. <view class="x-f">
  69. <text style="margin-right: 20px;">链接有效时长(分钟)</text>
  70. <u-input fontSize="14px" placeholder="链接有效时长" border="none" v-model="time" maxlength="5"></u-input>
  71. </view>
  72. </view>
  73. </u-modal>
  74. <u-notify ref="uNotify" message=""></u-notify>
  75. <!-- 生成海报 -->
  76. <u-popup :show="setImg" @close="closeimg" :round="12" style="z-index: 999;">
  77. <view class="w100 h540 center">
  78. <image :src="codeLink.url" mode="aspectFill" ></image>
  79. </view>
  80. <view class="justify-around mb40">
  81. <!-- <view class="column justify-center align-center" @click="shareimg">
  82. <image src='https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/image/wechat.png' class="w80 h80"></image>
  83. <view class="mt10">微信好友</view>
  84. </view> -->
  85. <view class="column justify-center align-center" @click="downimg">
  86. <image src='https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/image/downicon.png' class="w80 h80"></image>
  87. <view class=" mb10">长按图片保存</view>
  88. </view>
  89. </view>
  90. </u-popup>
  91. <u-overlay :show="showzhidao" @click="showzhidao = false" style="z-index: 9999;">
  92. <view class="point-box">
  93. <view class="imgshe">
  94. <image src='https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/image/point.png' class="w300 h300"></image>
  95. </view>
  96. <view class="column colorf fs32 xu-box fs40
  97. align-center justify-center">
  98. <view class="justify-center">点击右上角
  99. <image src="https://fbylive.obs.cn-southwest-2.myhuaweicloud.com:443/app/image/wxmore.png" class="w50 h50 mlr10"></image>
  100. </view>
  101. <view class="mt20">选择 “转发给朋友”</view>
  102. <view style="color: #cbcbcb;" class="fs28 mt40">点击任意位置关闭弹窗</view>
  103. </view>
  104. </view>
  105. </u-overlay>
  106. </view>
  107. </template>
  108. <script>
  109. import {
  110. sharecourselink,
  111. buildCode,
  112. getSDK
  113. } from '@/api/courseManage'
  114. import html2canvas from 'html2canvas'
  115. import wx from 'weixin-js-sdk'
  116. export default {
  117. props: {
  118. activeTab: {
  119. type: [Number, String],
  120. default: 0
  121. },
  122. // 来源
  123. from: {
  124. type: String,
  125. default: 'live'
  126. },
  127. info: {
  128. type: Object,
  129. default: () => {
  130. return {}
  131. }
  132. },
  133. parentMethod:{
  134. type: Function
  135. }
  136. },
  137. data() {
  138. return {
  139. cavansimg: false,
  140. showShare: false,
  141. setTimeShow: false,
  142. time: "",
  143. user: [],
  144. type: 1,
  145. copylink: '',
  146. setImg: '',
  147. codeLink: '',
  148. setImg: false,
  149. painterId: 'myPainter',
  150. isLongPress: false,
  151. painterSrc: '',
  152. showzhidao: false,
  153. imgs:'https://fbylive.obs.cn-southwest-2.myhuaweicloud.com/fs/20250416/1744811256845.png'
  154. }
  155. },
  156. onLoad() {},
  157. mounted() {
  158. this.user = uni.getStorageSync("companyUserInfo") ? JSON.parse(uni.getStorageSync("companyUserInfo")) : {}
  159. },
  160. methods: {
  161. handleClick(){
  162. this.parentMethod();
  163. this.$emit('trigger-share', {
  164. title: this.info.courseName+this.info.title,
  165. path: "/pages_course/viedo",
  166. onshow:true,
  167. params: { companyId: this.user.companyId, companyUserId: this.user.userId,
  168. courseId:this.info.courseId,videoId:this.info.courseId },
  169. img:this.info.thumbnail||this.imgs
  170. });
  171. },
  172. // 获取jssdk
  173. getjssdklist() {
  174. const param = {
  175. url: window.location.href.split('#')[0] // 注意去除 hash
  176. }
  177. getSDK(param).then(res => {
  178. wx.config({
  179. debug: false,
  180. appId: res.data.appId, // 必填,公众号的唯一标识
  181. timestamp: res.data.timestamp, // 必填,生成签名的时间戳
  182. nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
  183. signature: res.data.signature, // 必填,签名
  184. jsApiList: ["updateAppMessageShareData", "onMenuShareAppMessage", ] // 必填,需要使用的JS接口列表
  185. });
  186. })
  187. },
  188. shareimg() {
  189. let self = this
  190. //分享好友
  191. // 配置--配置全局
  192. console.log(self.info.thumbnail + self.copylink, 11111)
  193. wx.ready(function() { //需在用户可能点击分享按钮前就先调用
  194. wx.updateAppMessageShareData({
  195. title: self.info.courseName + self.info.title, // 分享标题
  196. desc: self.info.description, // 分享描述
  197. // link: self.copylink,
  198. link: self.copylink,
  199. // 分享链接,该链接域名或路径必须与当前页面对应的公众 号JS安全域名一致
  200. imgUrl: self.info.thumbnail||self.imgs, // 分享图标
  201. success: function(res) {
  202. console.log(self.info, '456');
  203. self.showzhidao = true
  204. self.setImg = false
  205. self.showShare = false
  206. // 设置成功
  207. uni.showToast({
  208. title: '卡片已生成',
  209. icon: 'none',
  210. duration: 1000
  211. });
  212. },
  213. })
  214. // 另外一个方法·
  215. wx.onMenuShareAppMessage({
  216. title: self.info.courseName + self.info.title, // 分享标题
  217. desc: self.info.title, // 分享描述
  218. link: self.copylink,
  219. // 分享链接,该链接域名或路径必须与当前页面对应的公众 号JS安全域名一致
  220. imgUrl: self.info.thumbnail, // 分享图标
  221. success: function(res) {
  222. console.log(self.info, '456');
  223. self.showzhidao = true
  224. self.setImg = false
  225. self.showShare = false
  226. // 设置成功
  227. uni.showToast({
  228. title: '卡片已生成',
  229. icon: 'none',
  230. duration: 1000
  231. });
  232. },
  233. // complete: function(res) {
  234. // uni.showToast({
  235. // title: JSON.stringify(res),
  236. // icon: 'none',
  237. // duration: 2000
  238. // })
  239. // }
  240. })
  241. // wx.error((res) => {
  242. // console.log('wx.error:', res);
  243. // })
  244. });
  245. },
  246. downimg() {
  247. },
  248. handleLongPress() {
  249. this.isLongPress = true;
  250. // 延时执行保存操作,避免误触
  251. setTimeout(() => {
  252. if (this.isLongPress) {
  253. this.saveImage();
  254. }
  255. }, 1000); // 1000毫秒后执行保存操作
  256. },
  257. saveImage() {
  258. const painter = this.$painter.getPainter(this.painterId);
  259. painter.saveImage('jpg', (path) => {
  260. uni.saveImageToPhotosAlbum({
  261. filePath: path,
  262. success: () => {
  263. uni.showToast({
  264. title: '保存成功'
  265. });
  266. },
  267. fail: () => {
  268. uni.showToast({
  269. title: '保存失败',
  270. icon: 'none'
  271. });
  272. }
  273. });
  274. }, 'myCanvas');
  275. },
  276. closeimg() {
  277. this.setImg = false
  278. this.showShare = false
  279. },
  280. closetext() {
  281. this.setTimeShow = false
  282. },
  283. buildimg() {
  284. this.buildimgAcode()
  285. },
  286. //生成海报和二维码
  287. buildimgAcode() {
  288. uni.showLoading({
  289. title: '正在生成中...'
  290. })
  291. buildCode({
  292. companyId: this.user.companyId,
  293. companyUserId: this.user.userId,
  294. courseId: this.info.courseId,
  295. effectiveDuration: this.time,
  296. videoId: this.info.videoId,
  297. imgUrl:this.info.thumbnail,
  298. title:this.info.title,
  299. duration:this.info.duration
  300. }).then(res => {
  301. if (res.code == 200) {
  302. this.codeLink = res.posterImage
  303. this.setImg = true
  304. this.getlink()
  305. console.log(this.codeLink)
  306. } else {
  307. uni.showToast({
  308. icon: 'none',
  309. title: res.msg
  310. })
  311. }
  312. })
  313. },
  314. toCourseDetail(info) {
  315. uni.navigateTo({
  316. // url: '/pages/courseManage/course/learning?course='+JSON.stringify(info)
  317. // url: `/pages/courseManage/course/learning?course=${JSON.stringify(info)}&isvip=1`
  318. url:'/pages/courseManage/course/courseVideo?videoId='+info.videoId
  319. })
  320. },
  321. handleShare() {
  322. // #ifdef H5
  323. this.getjssdklist()
  324. this.showShare = true
  325. // #endif
  326. // this.getlink('preload'); // 提前加载链接
  327. },
  328. closeShare() {
  329. this.showShare = false
  330. // console.log('open');
  331. },
  332. openShare() {
  333. // this.showShare = false
  334. // console.log('close');
  335. },
  336. copyLink() {
  337. this.setTimeShow = true
  338. },
  339. confirmTime() {
  340. this.setTimeShow = !this.setTimeShow
  341. this.showShare = !this.showShare
  342. const params = {
  343. companyId: this.user.companyId,
  344. companyUserId: this.user.userId,
  345. courseId: this.info.courseId,
  346. time: this.time,
  347. videoId: this.info.videoId,
  348. }
  349. sharecourselink(params).then(res => {
  350. if (res.code == 200) {
  351. this.copylink = res.url
  352. // if (this.copylink.startsWith('http://')) {
  353. // this.copylink = this.copylink.replace('http://', 'https://');
  354. // }
  355. // console.log(this.copylink)
  356. setTimeout(() => {
  357. uni.setClipboardData({
  358. data: this.copylink,
  359. success: () => {
  360. uni.showToast({
  361. title: '链接已复制',
  362. icon: 'none',
  363. duration: 2000
  364. });
  365. },
  366. fail: () => {
  367. uni.showToast({
  368. title: '复制失败',
  369. icon: 'none'
  370. });
  371. }
  372. });
  373. }, 100)
  374. console.log(this.copylink)
  375. } else {
  376. uni.showToast({
  377. icon: 'none',
  378. title: res.msg
  379. })
  380. }
  381. })
  382. },
  383. getlink(type) {
  384. const params = {
  385. companyId: this.user.companyId,
  386. companyUserId: this.user.userId,
  387. courseId: this.info.courseId,
  388. time: this.time,
  389. // type: this.type,
  390. videoId: this.info.videoId,
  391. }
  392. sharecourselink(params).then(res => {
  393. if (res.code == 200) {
  394. this.copylink = res.url
  395. // 强制使用 HTTPS
  396. if (this.copylink.startsWith('http://')) {
  397. this.copylink = this.copylink.replace('http://', 'https://');
  398. }
  399. } else {
  400. uni.showToast({
  401. icon: 'none',
  402. title: res.msg
  403. })
  404. }
  405. })
  406. },
  407. handleStatistics() {
  408. const info = {
  409. courseId: this.info.courseId,
  410. title: this.info.title,
  411. thumbnail: this.info.thumbnail,
  412. createTime: this.info.createTime,
  413. duration: this.info.duration,
  414. fileId: this.info.fileId,
  415. courseName: this.info.courseName,
  416. videoId:this.info.videoId,
  417. }
  418. uni.navigateTo({
  419. url: '/pages/courseManage/statistics?info=' + JSON.stringify(info)
  420. })
  421. },
  422. copyId() {
  423. uni.setClipboardData({
  424. data: this.info.title,
  425. success: () => {
  426. // this.$refs.uNotify.show({
  427. // top: 0,
  428. // type: 'success',
  429. // // color: '#000',
  430. // // bgColor: '#e8e8e8',
  431. // message: '复制课程标题成功',
  432. // duration: 1000 * 2,
  433. // fontSize: 20,
  434. // safeAreaInsetTop: true
  435. // })
  436. uni.showToast({
  437. icon: 'none',
  438. title: '复制课程标题成功'
  439. })
  440. }
  441. });
  442. }
  443. }
  444. }
  445. </script>
  446. <style scoped lang="scss">
  447. .card-share{
  448. position: relative;
  449. }
  450. .share{
  451. display: inline-block;
  452. position: absolute;
  453. top: 0;
  454. left: 0;
  455. width: 100%;
  456. height: 100%;
  457. opacity: 0;
  458. }
  459. .imgshe {
  460. display: flex;
  461. flex-direction: row-reverse
  462. }
  463. .point-box {
  464. height: 100%;
  465. width: 100%;
  466. .xu-box {
  467. border: #f5f5f5 4rpx dashed;
  468. padding: 20rpx 20rpx;
  469. }
  470. }
  471. #codeurl {
  472. position: relative;
  473. }
  474. ::v-deep {
  475. .model .u-fade-enter-active {
  476. z-index: 10075 !important;
  477. }
  478. }
  479. .sharePop {
  480. background-color: #fff;
  481. padding: 30rpx 0;
  482. border-radius: 20px 20px 0 0;
  483. /* #ifdef MP-WEIXIN */
  484. /* #endif */
  485. /* #ifdef H5 */
  486. padding-bottom: 60px;
  487. /* #endif */
  488. &-item {
  489. padding: 0 10px;
  490. box-sizing: border-box;
  491. font-family: PingFang SC, PingFang SC;
  492. font-weight: 400;
  493. font-size: 14px;
  494. display: inline-flex !important;
  495. image {
  496. height: 48px;
  497. width: 48px;
  498. margin-bottom: 10px;
  499. }
  500. }
  501. }
  502. .setTimebox {
  503. font-family: PingFang SC, PingFang SC;
  504. font-weight: 400;
  505. font-size: 14px;
  506. }
  507. .timetip {
  508. font-family: PingFang SC, PingFang SC;
  509. font-weight: 400;
  510. font-size: 14px;
  511. color: #2979ff;
  512. text-align: center;
  513. margin-bottom: 5px;
  514. }
  515. .courselist {
  516. font-family: PingFang SC, PingFang SC;
  517. font-weight: 400;
  518. font-size: 14px;
  519. &-item {
  520. width: 100%;
  521. border-radius: 14px;
  522. background-color: #fff;
  523. overflow: hidden;
  524. margin-bottom: 10px;
  525. }
  526. &-con {
  527. padding: 10px 10px 5px 10px;
  528. font-size: 12px;
  529. color: #777;
  530. }
  531. &-con-r {
  532. flex: 1;
  533. overflow: hidden;
  534. .more-t {
  535. flex: 1;
  536. font-size: 14px;
  537. color: #222;
  538. display: inline;
  539. }
  540. image {
  541. width: 20px;
  542. height: 20px;
  543. }
  544. .btn_icon {
  545. font-size: 14px;
  546. color: #1677ff;
  547. display: inline-flex;
  548. align-items: center;
  549. }
  550. }
  551. &-img {
  552. width: 110px;
  553. height: 70px;
  554. border-radius: 10px;
  555. overflow: hidden;
  556. flex-shrink: 0;
  557. margin-right: 10px;
  558. position: relative;
  559. image {
  560. height: 100%;
  561. width: 100%;
  562. }
  563. .status {
  564. position: absolute;
  565. top: 0;
  566. left: 0;
  567. z-index: 2;
  568. height: 21px;
  569. padding: 0 5px;
  570. box-sizing: border-box;
  571. line-height: 21px;
  572. border-radius: 10px 0 10px 0;
  573. text-align: center;
  574. color: #fff;
  575. background-color: #08ce36;
  576. }
  577. }
  578. &-desc {
  579. flex: 1;
  580. margin-top: 7px;
  581. }
  582. &-con-timebox {
  583. margin-top: 7px;
  584. flex-wrap: wrap;
  585. .acea-row {
  586. margin-right: 12px;
  587. margin-bottom: 5px;
  588. flex-wrap: nowrap;
  589. }
  590. .icon {
  591. margin-right: 5px;
  592. }
  593. }
  594. &-footer {
  595. padding: 5px;
  596. font-size: 14px;
  597. &-item {
  598. flex: 1;
  599. text-align: center;
  600. color: #1677ff;
  601. padding: 6px;
  602. box-sizing: border-box;
  603. }
  604. .shishi {
  605. border-left: 1px solid #f5f5f5;
  606. }
  607. .shuju {
  608. border-radius: 5px;
  609. border: 1px solid #1677ff;
  610. }
  611. }
  612. }
  613. </style>