live.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. <template>
  2. <view class="live column flex-1">
  3. <view class="videolist" v-if="upDown">
  4. <view class="vedio">
  5. <!-- <view class="p20 textscll">
  6. <u-notice-bar :icon="icon" :text="text1" mode="closable" bgColor='rgba(0,0,0,0.7)' color='#fff'
  7. style="border-radius: 10rpx;"></u-notice-bar>
  8. </view> -->
  9. <video id="myVideo" class="videotop" :src="path+livedata.videoUrl" :autoplay="autoplay"
  10. :controls='false' :poster='livedata.liveImgUrl' vslide-gesture-in-fullscreen='true'
  11. :show-center-play-btn="false"></video>
  12. </view>
  13. <view class="popup-video" v-if="!autoplay">
  14. <view class="fs36 bold" v-if="livedata.status==1">——直播还未开始——</view>
  15. <view class="fs36 bold" v-if="livedata.status==3">——直播已经结束——</view>
  16. <view class="fs28 mtb20">了解更多,点击下方联系老师</view>
  17. <view class="more" @click="showadd=!showadd">联系老师</view>
  18. </view>
  19. </view>
  20. <view class="justify-between align-center bgf" style="border-bottom: #dedede solid 2rpx;">
  21. <u-tabs :list="list1" @click="tabClick" inactiveStyle="color:#888"
  22. itemStyle='width:120rpx;height: 80rpx;'></u-tabs>
  23. <view class="justify-around flex-1 align-center">
  24. <view class="reflash" @click="getliving()">刷新</view>
  25. <image src='../../static/images/top.png' class="wh48" v-show="upDown" @click="upDown=!upDown"></image>
  26. <image src='../../static/images/up-down.png' class="wh48" v-show="!upDown" @click="upDown=!upDown">
  27. </image>
  28. </view>
  29. </view>
  30. <view class="talkbox column flex-1 hidden" style="height: 100%;" v-if="acttab==0">
  31. <!-- <u-notice-bar :icon="icon2" :text="text2" mode="closable" bgColor='rgba(254,253,235)' color='#FF5C03'
  32. style=" height: 30rpx;line-height: 40rpx;"></u-notice-bar> -->
  33. <scroll-view scroll-y="true" class="talk p20 scrolly flex-1 column"
  34. style="width: calc(100% - 40rpx);height: calc(100% - 40rpx);" :scroll-into-view="scrollIntoView">
  35. <view class="list justify-start" v-for="(item,index) in talklist" :key="item.index"
  36. :id="`list_${index}`" v-show="item.cmd=='sendMsg'">
  37. <u-avatar :src="item.avatar" size='30'></u-avatar>
  38. <view class=" ml16">
  39. <view class="fs24">
  40. <text style="color: #3fc69b;
  41. transform: scale(0.8);display: inline-block; "
  42. v-if="item.userId==userinfo.userId&&item.cmd=='sendMsg'">[ 我 ]</text>
  43. <text style="color: #c84e1e;
  44. transform: scale(0.8);display: inline-block;" v-if="item.userType==1">
  45. [ 管理员 ]</text>
  46. <text>{{item.nickName}}</text>
  47. </view>
  48. <view class="talktext bgf p20 mt12">
  49. <view class='fs24'>{{item.msg}}</view>
  50. </view>
  51. </view>
  52. </view>
  53. <view v-if="showWelcomeMessage" class="welcome-message">
  54. <view class="list justify-start" v-for="(item,index) in talklist" :key="item.index"
  55. :id="`list_${index}`" v-show="item.cmd=='entry'||item.cmd=='out'">
  56. <view class=" ml16">
  57. <view class="fs24">
  58. <text style="color: #c84e1e;
  59. transform: scale(0.8);display: inline-block;" v-if="item.userType==1">
  60. [ 管理员 ]</text>
  61. </view>
  62. <view class="talktext bgf p20 mt12 justify-start">
  63. <text style="color: #9e5584;" class="fs24 ">{{item.nickName}} </text>
  64. <view class='fs24 flex-1'>{{item.msg}}直播间</view>
  65. </view>
  66. </view>
  67. </view>
  68. </view>
  69. </scroll-view>
  70. <view class="pb90 mb20"></view>
  71. <view class="bot_talk bgf">
  72. <u-input :placeholder="placeholderText" border="none"
  73. customStyle='background:#f1f7f7;padding:12rpx;padding-left:40rpx' v-model="value" shape='circle'
  74. ></u-input>
  75. <!-- :disabled='talkdisabled'></u-input> -->
  76. <view class="sent ml20" @click="sendMsg">发送</view>
  77. </view>
  78. </view>
  79. <!-- <view class="answerbox" v-if="acttab==1">
  80. 问答
  81. </view> -->
  82. <view class="informationbox" v-if="acttab==1">
  83. <view class="p20 bgf m20 radius20">
  84. <view v-html="livedata.liveDesc"></view>
  85. <view v-show="!livedata.liveDesc">暂无资料</view>
  86. </view>
  87. </view>
  88. <view class="invite-member" @click="addwechat">
  89. <image src="../../static/images/wechat.png" class="wh80 weimg"></image>
  90. <view class="addwe">加微信</view>
  91. </view>
  92. <view class="">
  93. <u-popup :show="showadd" @close="close" @open="open" round='20rpx' bgColor='#fffee1'>
  94. <view class="addchat p20">
  95. <view class="u-flex-row-reverse u-flex">
  96. <u-icon name="close" size="18" @click="showadd=!showadd"></u-icon>
  97. </view>
  98. <view class="column align-center">
  99. <view class="fs36" style="color: #ff5c03;">扫码添加助教老师</view>
  100. <view class="fs28 color6">扫码添加助教老师</view>
  101. <view class="p10 mt40" style="border: #ff5c03 solid 2rpx;">
  102. <image :src="codeimg" class="wh180" @touchstart="longPress" @touchend="cancelLongPress">
  103. </image>
  104. </view>
  105. <view class="color6 mt20">长按识别二维码</view>
  106. </view>
  107. </view>
  108. </u-popup>
  109. </view>
  110. </view>
  111. </template>
  112. <script>
  113. import {
  114. getlive,
  115. gettextlist
  116. } from '@/api/home'
  117. // var wsUrl = "ws://live.ylrzcloud.com/socket/app/webSocket";
  118. var wsUrl = "ws://192.168.10.170:7114/app/webSocket";
  119. var pingpangTimes = null;
  120. var initTimes = null;
  121. var isSocketOpen = false;
  122. var socket = null;
  123. export default {
  124. data() {
  125. return {
  126. icon: '公告:',
  127. icon2: '广播:',
  128. text1: '组件功能丰富多端兼容让您快速集成开箱即用',
  129. text2: '丰富多端兼容让您快速集成开箱即用',
  130. srcAvatar: '',
  131. acttab: 0,
  132. value: '',
  133. talkdisabled: false, //是否禁用
  134. placeholderText: "请输入内容",
  135. showadd: false,
  136. autoplay: true, //自动播放
  137. userinfo: '', //用户信息
  138. path: 'http://192.168.10.170/dev-api',
  139. livedata: {},
  140. bufferRate: 0, // 缓冲时间
  141. playDuration: 0, //视频播放时间
  142. videoContext: '',
  143. thistime: uni.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss'),
  144. codeimg: '',
  145. userid: '4',
  146. liveId: '5',
  147. upDown: true, //是否视频显示隐藏
  148. isLongPress: false, // 是否长按
  149. timeout: null, // 计时器
  150. list1: [{
  151. name: '讨论'
  152. },
  153. // {
  154. // name: '问答'
  155. // },
  156. {
  157. name: '资料'
  158. },
  159. ],
  160. talklist: [],
  161. scrollIntoView: '',
  162. showWelcomeMessage: false,
  163. messageContent: ""
  164. }
  165. },
  166. mounted() {
  167. this.initSocket()
  168. var that = this;
  169. uni.$on('initSocket', () => {
  170. that.initSocket()
  171. })
  172. uni.$on('sendMsg', (item) => {
  173. that.sendMsg(item)
  174. })
  175. uni.$on('closeWebSocket', () => {
  176. that.closeWebSocket()
  177. })
  178. this.getEWechatSdk();
  179. this.userinfo = JSON.parse(uni.getStorageSync("userInfo"))
  180. this.getliving()
  181. this.gettalklist()
  182. },
  183. onReady: function(res) {
  184. this.videoContext = uni.createVideoContext('myVideo')
  185. // console.log(this.videoContext)
  186. },
  187. onLoad() {
  188. },
  189. methods: {
  190. gettalklist() {
  191. const param = {
  192. id: this.liveId
  193. }
  194. gettextlist(param).then(res => {
  195. if (res.code == 200) {
  196. this.talklist = res.data
  197. // console.log(res.data);
  198. this.$nextTick(() => {
  199. this.scrollIntoView = `list_${this.talklist.length-1}`
  200. })
  201. }
  202. })
  203. },
  204. longPress() {
  205. this.timeout = setTimeout(() => {
  206. this.isLongPress = true;
  207. // 执行保存图片的操作
  208. uni.saveImageToPhotosAlbum({
  209. filePath: this.livedata.qwQrCode, // 图片的本地路径或网络路径
  210. success: () => {
  211. uni.showToast({
  212. title: '保存成功'
  213. });
  214. },
  215. fail: () => {
  216. uni.showToast({
  217. title: '保存失败',
  218. icon: 'none'
  219. });
  220. }
  221. });
  222. }, 500); // 设置长按的阈值时间,这里是500毫秒
  223. },
  224. cancelLongPress() {
  225. clearTimeout(this.timeout);
  226. this.isLongPress = false;
  227. },
  228. getliving() {
  229. this.gettalklist()
  230. const param = {
  231. id: this.liveId
  232. }
  233. getlive(param).then(res => {
  234. if (res.code == 200) {
  235. this.livedata = res.data
  236. this.codeimg = res.data.qwQrCode
  237. if (this.livedata.status == 2) {
  238. this.autoplay = true
  239. this.videoContext.seek(this.livedata.nowDuration)
  240. // console.log(this.autoplay)
  241. } else {
  242. this.autoplay = false
  243. this.placeholderText = "直播开始才能发言讨论"
  244. this.talkdisabled = true
  245. }
  246. // console.log(this.autoplay)
  247. } else {
  248. uni.showToast({
  249. title: res.msg,
  250. icon: 'none',
  251. duration: 2000
  252. });
  253. }
  254. })
  255. },
  256. addwechat() {
  257. this.showadd = !this.showadd
  258. },
  259. open() {
  260. },
  261. close() {
  262. this.showadd = !this.showadd
  263. },
  264. tabClick(e) {
  265. this.acttab = e.index
  266. if(e.index==0){
  267. this.$nextTick(() => {
  268. this.gettalklist()
  269. })
  270. }
  271. },
  272. getEWechatSdk() {
  273. let eWechatSdk = ''
  274. if (/(Android)/i.test(navigator.userAgent)) {
  275. eWechatSdk = 'jWeixin'
  276. } else if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
  277. eWechatSdk = 'wx'
  278. } else {
  279. eWechatSdk = 'jWeixin'
  280. }
  281. uni.setStorageSync("wxSdk", eWechatSdk)
  282. },
  283. closeWebSocket() {
  284. if (socket != null) {
  285. uni.closeSocket();
  286. }
  287. clearInterval(pingpangTimes)
  288. },
  289. reConnect() {
  290. var that = this;
  291. try {
  292. uni.closeSocket();
  293. } catch (e) {
  294. }
  295. setTimeout(function() {
  296. that.initSocket();
  297. }, 10000);
  298. },
  299. initSocket() {
  300. var that = this;
  301. // console.log("initSocket")
  302. //创建一个socket连接
  303. socket = uni.connectSocket({
  304. // url: wsUrl+"?userId=40486&liveId=2",
  305. // url: wsUrl + "?userId=" + this.userid + "&liveId=" +
  306. // this.liveId+"&AppToken="+'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI0IiwiaWF0IjoxNzQwNzIxMDQ1LCJleHAiOjE3NDEzMjU4NDV9.PgS83JTMSJgVFD6vGAhUsEPbS6Az4yMyX8wVug61TZNTB3092CtvANo-AB9ZG4NdSBLgLrf4litM3vvTk-FK0g',
  307. url: wsUrl + "?userId=666"+ "&liveId=777" +"&AppToken="+'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2NjYiLCJpYXQiOjE3NTE4NzQ0NzEsImV4cCI6MTc4MzQxMDQ3MX0.3uxTTb0qXygmaY9ItovMclxJCNhNEi6kFEqmfLGg4lP2PYzCPODsVjW4PjXNu6EYsl5eYyESltHWcwBnaNkilQ&signature=ff21bfb41ddd5f2e31d6f5bf32ec565aab9c518614d139fa26727468ce701237&userType=0&timestamp=666',
  308. multiple: true,
  309. success: res => {
  310. //先确保清除了之前的心跳定时器
  311. clearInterval(pingpangTimes)
  312. uni.onSocketMessage((res) => {
  313. const redata = JSON.parse(res.data);
  314. // console.log(redata)
  315. // console.log(111,this.talklist)
  316. this.talklist.push(redata.data)
  317. this.$nextTick(() => {
  318. this.scrollIntoView = `list_${this.talklist.length-1}`
  319. })
  320. // console.log(222,this.talklist)
  321. // console.info(redata)
  322. if (redata.cmd == 'deleteId') {
  323. uni.$emit('deleteId');
  324. } else if (redata.cmd == 'init') {
  325. uni.$emit('init', redata.data);
  326. } else if (redata.cmd == 'reload') {
  327. uni.$emit('reload');
  328. } else if (redata.cmd == 'sendStatus') {
  329. uni.$emit('sendStatus', redata.data);
  330. }else if (redata.data.cmd == 'entry') {
  331. this.showWelcomeMessage=true
  332. uni.$emit('entry', redata.data);
  333. console.log(redata.data)
  334. }
  335. })
  336. },
  337. error: res => {
  338. uni.$emit('websocket', 0);
  339. // console.log(res)
  340. },
  341. })
  342. //监听socket打开
  343. uni.onSocketOpen(() => {
  344. isSocketOpen = true
  345. console.log('WebSocket连接已打开!!');
  346. // uni.$emit('websocket',1);
  347. uni.showToast({
  348. title: "插件已打开",
  349. icon: 'none',
  350. });
  351. })
  352. //监听socket关闭
  353. uni.onSocketClose(() => {
  354. isSocketOpen = false
  355. clearInterval(pingpangTimes)
  356. console.log('WebSocket连接已关闭!');
  357. // uni.$emit('websocket',0);
  358. uni.showToast({
  359. title: "插件离线",
  360. icon: 'none',
  361. });
  362. //重连
  363. that.reConnect()
  364. })
  365. //监听socket错误
  366. uni.onSocketError(() => {
  367. isSocketOpen = false
  368. clearInterval(pingpangTimes)
  369. console.log('WebSocket连接打开失败');
  370. // uni.$emit('websocket',0);
  371. uni.showToast({
  372. title: "插件离线",
  373. icon: 'none',
  374. });
  375. uni.showModal({
  376. content: '聊天连接失败是否重新尝试连接',
  377. success() {
  378. that.reConnect()
  379. }
  380. })
  381. })
  382. },
  383. sendMsg() {
  384. if (isSocketOpen) {
  385. const data = {
  386. liveId: this.livedata.liveId,
  387. userId: this.userinfo.userId,
  388. userType: 0,
  389. cmd: "sendMsg",
  390. msg: this.value,
  391. nickName: this.userinfo.nickName,
  392. avatar: this.userinfo.avatar
  393. }
  394. if(this.value==""){
  395. uni.showToast({
  396. title: "不能发送空消息",
  397. icon: 'none',
  398. });
  399. }else{
  400. socket.send({
  401. data: JSON.stringify(data),
  402. success: () => {
  403. // this.gettalklist()
  404. console.log("发送成功")
  405. this.value = ''
  406. },
  407. fail: () => {
  408. console.log("发送失败")
  409. }
  410. })
  411. }
  412. }
  413. },
  414. }
  415. }
  416. </script>
  417. <style lang="scss" scoped>
  418. .welcome-message {
  419. position: fixed;
  420. width: 100%;
  421. bottom: 120rpx;
  422. left: 50%;
  423. transform: translateX(-50%);
  424. color: #666;
  425. padding: 10px 20px;
  426. border-radius: 20px;
  427. animation: fadeOut 1s ease 1s forwards; /* 1秒后开始淡出 */
  428. z-index: 1000;
  429. }
  430. @keyframes fadeOut {
  431. from { opacity: 1; }
  432. to { opacity: 0; }
  433. }
  434. .live {
  435. height: 100%;
  436. overflow: hidden;
  437. }
  438. .list {
  439. width: 80%;
  440. margin-bottom: 20rpx;
  441. animation: xxxawdawd .2s;
  442. }
  443. @keyframes xxxawdawd {
  444. from {
  445. margin-top: 0rpx;
  446. opacity: 0;
  447. }
  448. to {
  449. margin-top: 20rpx;
  450. opacity: 1;
  451. }
  452. }
  453. .vedio {
  454. height: 500rpx;
  455. width: 100%;
  456. background-color: rgba(0, 0, 0, 0.6);
  457. position: relative;
  458. }
  459. .videotop {
  460. width: 100%;
  461. height: 100%;
  462. }
  463. .popup-video {
  464. position: absolute;
  465. top: 0;
  466. height: 500rpx;
  467. display: flex;
  468. flex-direction: column;
  469. align-items: center;
  470. justify-content: center;
  471. width: 100%;
  472. color: #fff;
  473. background-color: rgba(0, 0, 0, 0.6);
  474. .more {
  475. background-color: #3280fe;
  476. border-radius: 80rpx;
  477. width: 280rpx;
  478. text-align: center;
  479. height: 60rpx;
  480. line-height: 60rpx;
  481. }
  482. }
  483. .textscll {
  484. position: absolute;
  485. top: 0;
  486. z-index: 999;
  487. }
  488. .reflash {
  489. border: 2rpx solid #888;
  490. border-radius: 80rpx;
  491. padding: 4rpx 16rpx;
  492. color: #888;
  493. }
  494. .talktext {
  495. border-radius: 8rpx;
  496. view {
  497. width: 100%;
  498. }
  499. }
  500. .bot_talk {
  501. width: calc(100% - 40rpx);
  502. display: flex;
  503. justify-content: space-between;
  504. align-items: center;
  505. position: fixed;
  506. bottom: 0;
  507. padding: 20rpx;
  508. .sent {
  509. width: 120rpx;
  510. text-align: center;
  511. height: 60rpx;
  512. line-height: 60rpx;
  513. background-color: #3280fe;
  514. color: #fff;
  515. border-radius: 40rpx;
  516. font-size: 24rpx;
  517. }
  518. }
  519. .invite-member {
  520. height: 55px;
  521. width: 50px;
  522. position: fixed;
  523. bottom: 80px;
  524. right: 10px;
  525. cursor: pointer;
  526. .weimg {
  527. position: relative;
  528. }
  529. .addwe {
  530. position: absolute;
  531. left: -7%;
  532. bottom: 4rpx;
  533. color: #fff;
  534. background-color: #fc285c;
  535. font-size: 24rpx;
  536. border-radius: 20rpx;
  537. padding: 4rpx 12rpx;
  538. }
  539. }
  540. .addchat {
  541. height: 450rpx;
  542. }
  543. </style>