courseItem.vue 16 KB

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