courseItem.vue 16 KB

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