roomDetail.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. <template>
  2. <div id="roomsDetail">
  3. <el-row>
  4. <el-col :span="24" class="video"> </el-col>
  5. </el-row>
  6. <van-overlay :show="show" style="{ height: oheight, width: '100%' }">
  7. <div id="transformid" class="wrapper" :style="{ transform: transform, height: vheight, width: vwidth, padding: vpadding, position: 'absolute' }">
  8. <div :style="{ height: lheight, width: lwidth }" id="look-video-left" class="video-box col-div look-video-left"></div>
  9. <div id="look-video-right" :style="{ height: rheight, width: rwidth }" class="video-box col-div look-video-right"></div>
  10. </div>
  11. <div class="videoBtn" id="videobtnid" :style="{ top: btop, right: bright }">
  12. <el-button type="warning" round @click="full()" size="mini"><i class="el-icon-rank"></i></el-button>
  13. </div>
  14. </van-overlay>
  15. <el-row class="chatrow">
  16. <el-col :span="24" class="info">
  17. <el-col :span="12" class="num">
  18. <span>观看:{{ total }}</span>
  19. </el-col>
  20. <el-col :span="12" class="meetBtn">
  21. <el-button v-show="switchbtn" type="warning" round size="small" @click="roomMeetBtn()">互动<i class="el-icon-camera el-icon--right"></i></el-button>
  22. </el-col>
  23. </el-col>
  24. </el-row>
  25. <van-tabs type="card" @click="tabsClick">
  26. <van-tab title="会议简介">
  27. <van-col :span="24">
  28. <van-col class="content">{{ roomInfos.content }}</van-col>
  29. </van-col>
  30. <van-col :span="24">
  31. <van-swipe :autoplay="3000">
  32. <van-swipe-item v-for="(advert, index) in roomInfos.adverts" :key="index">
  33. <img width="100%" height="100px" v-lazy="advert.imgdir" @click="imgclick(advert.imgurl)" />
  34. </van-swipe-item>
  35. </van-swipe>
  36. </van-col>
  37. </van-tab>
  38. <van-tab title="互动留言">
  39. <el-row>
  40. <el-col :span="24" class="info">
  41. <el-col :span="24" class="chat">
  42. <el-col :span="24" class="chatInfo" id="chatSell">
  43. <el-col :span="24" class="list" v-for="(item, index) in dataList" :key="index">
  44. <p>
  45. <span :class="item.sendname == user.name ? 'selfColor' : ''">{{ item.sendname }}:</span>
  46. <span>{{ item.content }}</span>
  47. </p>
  48. </el-col>
  49. </el-col>
  50. </el-col>
  51. <el-col :span="24" class="submit">
  52. <el-col :span="19" class="input">
  53. <el-input type="textarea" maxlength="5000" show-word-limit v-model="content"></el-input>
  54. </el-col>
  55. <el-col :span="5" class="btn">
  56. <el-button type="primary" size="mini" @click="chatCreate">发送</el-button>
  57. </el-col>
  58. </el-col>
  59. </el-col>
  60. </el-row>
  61. </van-tab>
  62. <van-tab title="参与成员">
  63. <van-col :span="24" class="userList">
  64. <van-grid :column-num="3">
  65. <van-grid-item v-for="(item, index) in userList" :key="index" icon="manager" :text="item.username" />
  66. </van-grid>
  67. </van-col>
  68. </van-tab>
  69. <van-tab title="申请评分">
  70. <van-col :span="24">
  71. <van-field v-model="isxf" name="isxf" left-icon="star-o" label="学分" type="number" placeholder="请填写需要授予学分" />
  72. </van-col>
  73. <van-col :span="24" style="text-align:center">
  74. <van-button icon="star-o" type="primary" @click="sqClick">
  75. 申请
  76. </van-button>
  77. </van-col>
  78. </van-tab>
  79. </van-tabs>
  80. <van-popup v-model="showQuest" position="bottom">
  81. <van-col :span="24" class="questtitle">
  82. <span>{{ questInfo.name }}</span>
  83. </van-col>
  84. <van-col class="questpopup" :span="24" v-for="(item, index) in questInfo.question" :key="index">
  85. <p style="margin:5px 0;">
  86. {{ index + 1 }}({{ item.type == '0' ? '单选' : item.type == '1' ? '多选' : item.type == '2' ? '问答' : '未识别' }})、{{ item.topic }}
  87. </p>
  88. <span v-if="item.type == '0'">
  89. <van-radio-group v-model="item.answer">
  90. <van-radio v-for="(i, ri) in item.option" :key="`${index}-${ri}`" :name="i.opname">{{ i.opname }}</van-radio>
  91. </van-radio-group>
  92. </span>
  93. <span v-if="item.type == '1'">
  94. <van-checkbox-group v-model="item.answer">
  95. <van-checkbox shape="square" v-for="(i, ci) in item.option" :key="`${index}-${ci}`" :name="i.opname">{{ i.opname }}</van-checkbox>
  96. </van-checkbox-group>
  97. </span>
  98. <span v-if="item.type == '2'">
  99. <van-field v-model="item.answer" type="textarea" rows="2" autosize placeholder="请输入" maxlength="200" show-word-limit />
  100. </span>
  101. </van-col>
  102. <van-col :span="24" class="questbtn">
  103. <van-button color="linear-gradient(to right, #4bb0ff, #6149f6)" @click="submitQuest">提交</van-button>
  104. </van-col>
  105. </van-popup>
  106. </div>
  107. </template>
  108. <script>
  109. import TRTC from 'trtc-js-sdk';
  110. import { mapState, createNamespacedHelpers } from 'vuex';
  111. import Vue from 'vue';
  112. import { Swipe, SwipeItem, Lazyload } from 'vant';
  113. import { Image as VanImage } from 'vant';
  114. import wx from 'weixin-js-sdk';
  115. Vue.use(Swipe);
  116. Vue.use(SwipeItem);
  117. Vue.use(VanImage);
  118. Vue.use(Lazyload);
  119. const { mapActions: uploadquestion } = createNamespacedHelpers('uploadquestion');
  120. const { mapActions: gensign } = createNamespacedHelpers('gensign');
  121. const { mapActions: room } = createNamespacedHelpers('room');
  122. const { mapActions: quest } = createNamespacedHelpers('quest');
  123. const { mapActions: chat } = createNamespacedHelpers('chat');
  124. export default {
  125. name: 'roomsDetail',
  126. props: {},
  127. components: {},
  128. data: function() {
  129. return {
  130. client_: null,
  131. localStream_: null,
  132. sdkAppId_: '1400380125',
  133. userId_: '',
  134. roomInfos: {},
  135. sendmemo: '',
  136. total: 0,
  137. transform: 'rotate(0)',
  138. oheight: '200px',
  139. vwidth: '100%',
  140. vheight: '200px',
  141. rheight: '200px',
  142. rwidth: '30%',
  143. lheight: '200px',
  144. lwidth: '70%',
  145. show: true,
  146. vpadding: '0',
  147. btop: '167px',
  148. bright: '10px',
  149. isscreen: false,
  150. content: '',
  151. dataList: [],
  152. lvideoid_: '',
  153. rvideoid_: '',
  154. userList: [],
  155. isxf: '',
  156. showQuest: false,
  157. queid: '',
  158. questInfo: {},
  159. switchbtn: false,
  160. };
  161. },
  162. created() {
  163. this.initclient();
  164. this.lookuserSearch();
  165. this.lookusercountsel();
  166. this.chatSearch();
  167. this.userswichrole();
  168. },
  169. mounted() {
  170. this.channel();
  171. },
  172. destroyed() {
  173. const that = this;
  174. window.addEventListener('pagehide', function() {
  175. console.log('页面要关闭了');
  176. that.recordSave();
  177. });
  178. },
  179. methods: {
  180. ...gensign(['gensignFetch']),
  181. ...room(['lookuserFetch', 'fetch', 'lookusercount', 'lookquery', 'lookupdate', 'lookrecord', 'lookuserswichrole']),
  182. ...chat(['query', 'create']),
  183. ...quest(['questfetch']),
  184. ...uploadquestion({ upcreate: 'create', upquery: 'query' }),
  185. async recordSave() {
  186. console.log(2121);
  187. let data = {};
  188. data.type = '1';
  189. data.roomid = this.id;
  190. data.roomname = this.roomname;
  191. data.userid = this.user.uid;
  192. data.username = this.user.name;
  193. const res = await this.lookrecord(data);
  194. },
  195. async sqClick() {
  196. let data = {};
  197. data.roomid = this.id;
  198. data.isxf = this.isxf;
  199. data.userid = this.user.uid;
  200. const res = await this.lookupdate(data);
  201. if (this.$checkRes(res)) {
  202. console.log(res.data);
  203. this.$message({
  204. message: '操作成功',
  205. type: 'success',
  206. });
  207. }
  208. },
  209. async tabsClick(name, title) {
  210. if (title === '参与成员') {
  211. this.lookuserQuery();
  212. }
  213. },
  214. async lookuserQuery({ skip = 0, limit = 1000 } = {}) {
  215. const info = { roomid: this.id };
  216. let res = await this.lookquery({ skip, limit, ...info });
  217. this.$set(this, `userList`, res.data);
  218. },
  219. async chatSearch({ skip = 0, limit = 1000 } = {}) {
  220. const info = { roomid: this.id };
  221. let res = await this.query({ skip, limit, ...info });
  222. this.$set(this, `dataList`, res.data);
  223. },
  224. async chatCreate() {
  225. let data = {};
  226. data.roomid = this.id;
  227. data.type = '0';
  228. data.content = this.content;
  229. data.sendid = this.user.uid;
  230. data.sendname = this.user.name;
  231. const res = await this.create(data);
  232. if (this.$checkRes(res)) {
  233. //this.$message('发送成功');
  234. this.content = '';
  235. }
  236. },
  237. channel() {
  238. console.log('in function:');
  239. this.$stomp({
  240. [`/exchange/public_chat_` + this.id]: this.onMessage,
  241. });
  242. this.$stomp({
  243. [`/exchange/quest_publish_` + this.id]: this.onQueMessage,
  244. });
  245. this.$stomp({
  246. [`/exchange/switch_role_` + this.user.uid]: this.onSwichMessage,
  247. });
  248. },
  249. onMessage(message) {
  250. // console.log('receive a message: ', message.body);
  251. let body = _.get(message, 'body');
  252. if (body) {
  253. body = JSON.parse(body);
  254. this.dataList.push(body);
  255. this.content = '';
  256. }
  257. this.$nextTick(() => {
  258. document.getElementById('chatSell').scrollTop = document.getElementById('chatSell').scrollHeight + 275;
  259. });
  260. },
  261. onQueMessage(message) {
  262. // console.log('receive a message: ', message.body);
  263. this.showQuest = true;
  264. let body = _.get(message, 'body');
  265. if (body) {
  266. body = JSON.parse(body);
  267. this.queid = body.queid;
  268. this.questSearch();
  269. }
  270. },
  271. onSwichMessage(message) {
  272. // console.log('receive a message: ', message.body);
  273. let body = _.get(message, 'body');
  274. if (body) {
  275. body = JSON.parse(body);
  276. if (body.switchrole === 'anchor' && body.userid === this.user.uid) {
  277. this.switchbtn = true;
  278. this.roomMeetBtn();
  279. } else {
  280. this.switchbtn = false;
  281. }
  282. }
  283. },
  284. async userswichrole() {
  285. // 根据房间id查询房间详细信息
  286. let result = await this.lookuserswichrole({ roomid: this.id, userid: this.user.uid });
  287. if (this.$checkRes(result)) {
  288. if (result.data && result.data.switchrole === 'anchor') {
  289. this.switchbtn = true;
  290. }
  291. }
  292. },
  293. roomMeetBtn() {
  294. const url = '/pages/meeting/meeting?uid=' + this.user.uid + '&username=' + this.user.name + '&rid=' + this.id + '&roomname=' + this.roomname;
  295. console.log(url);
  296. wx.miniProgram.navigateTo({ url });
  297. },
  298. async questSearch() {
  299. if (this.queid) {
  300. const result = await this.questfetch(this.queid);
  301. if (this.$checkRes(result)) {
  302. console.log(result.data);
  303. this.$set(this, `questInfo`, result.data);
  304. }
  305. }
  306. },
  307. async submitQuest() {
  308. let form = { roomid: this.id, userid: this.user.uid, questionnaireid: this.queid };
  309. let answers = [];
  310. for (const question of this.questInfo.question) {
  311. answers.push({ questionid: question._id, answer: JSON.stringify(question.answer) });
  312. }
  313. form.answers = answers;
  314. console.log(form);
  315. let res = await this.upcreate(form);
  316. if (res.errcode == '0') {
  317. this.showQuest = false;
  318. }
  319. },
  320. async lookuserSearch() {
  321. if (this.user.uid) {
  322. let data = {};
  323. data.roomid = this.id;
  324. data.roomname = this.roomname;
  325. data.userid = this.user.uid;
  326. data.username = this.user.name;
  327. const res = await this.lookuserFetch(data);
  328. }
  329. // 根据房间id查询房间详细信息
  330. let result = await this.fetch(this.id);
  331. if (this.$checkRes(result)) {
  332. console.log(result.data);
  333. this.$set(this, `roomInfos`, result.data);
  334. // 根据房间信息判断是否回答问卷
  335. if (result.data.isque === '1') {
  336. const info = { roomid: this.id, userid: this.user.uid, questionnaireid: result.data.queid };
  337. const skip = 0;
  338. const limit = 10;
  339. const upres = await this.upquery({ skip, limit, ...info });
  340. if (this.$checkRes(upres)) {
  341. if (upres.data.length === 0) {
  342. this.showQuest = true;
  343. this.queid = result.data.queid;
  344. this.questSearch();
  345. }
  346. }
  347. }
  348. }
  349. },
  350. async lookusercountsel() {
  351. // 根据房间id查询房间详细信息
  352. let result = await this.lookusercount({ roomname: this.roomname });
  353. if (this.$checkRes(result)) {
  354. console.log(result.total);
  355. this.$set(this, `total`, result.total);
  356. }
  357. },
  358. onSubmit() {
  359. console.log(this.sendmemo);
  360. },
  361. async initclient() {
  362. this.userId_ = this.user.uid;
  363. const res = await this.gensignFetch({ userid: this.userId_ });
  364. if (this.$checkRes(res)) {
  365. this.client_ = TRTC.createClient({
  366. mode: 'live',
  367. sdkAppId: this.sdkAppId_,
  368. userId: this.userId_,
  369. userSig: res.data,
  370. });
  371. await this.client_.join({ roomId: this.roomname, role: 'audience' });
  372. this.client_.on('stream-subscribed', event => {
  373. const remoteStream = event.stream;
  374. // 远端流订阅成功,播放远端音视频流
  375. console.log(remoteStream);
  376. this.show = true;
  377. console.log('111' + remoteStream.getType());
  378. console.log('111' + remoteStream.getUserId());
  379. console.log('111' + remoteStream.getId());
  380. const userid_ = remoteStream.getUserId();
  381. const res_ = userid_.indexOf('-');
  382. if (res_ === -1) {
  383. this.rvideoid_ = 'video_' + remoteStream.getId();
  384. remoteStream
  385. .play('look-video-right')
  386. .then(() => {
  387. // autoplay success
  388. })
  389. .catch(e => {
  390. const errorCode = e.getCode();
  391. if (errorCode === 0x4043) {
  392. // PLAY_NOT_ALLOWED,引导用户手势操作恢复音视频播放
  393. remoteStream.resume();
  394. }
  395. });
  396. } else {
  397. this.lvideoid_ = 'video_' + remoteStream.getId();
  398. remoteStream
  399. .play('look-video-left')
  400. .then(() => {
  401. // autoplay success
  402. // 播放成功时调用css属性
  403. const _lvideoid_ = document.getElementById(this.lvideoid_);
  404. const style = 'width: 100%; height: 100%; position: absolute; transform: rotateY(0); object-fit: fill';
  405. _lvideoid_.style.cssText = style;
  406. })
  407. .catch(e => {
  408. const errorCode = e.getCode();
  409. if (errorCode === 0x4043) {
  410. // PLAY_NOT_ALLOWED,引导用户手势操作恢复音视频播放
  411. remoteStream.resume();
  412. }
  413. });
  414. }
  415. });
  416. // 监听远端流增加事件
  417. this.client_.on('stream-added', event => {
  418. const remoteStream = event.stream;
  419. console.log('222' + remoteStream.getType());
  420. // 订阅远端音频和视频流
  421. this.client_.subscribe(remoteStream, { audio: true, video: true }).catch(e => {
  422. console.error('failed to subscribe remoteStream');
  423. });
  424. });
  425. this.client_.on('stream-removed', event => {
  426. const remoteStream = event.stream;
  427. console.log('stop----');
  428. remoteStream.stop();
  429. });
  430. }
  431. },
  432. imgclick(url) {
  433. location.href = url;
  434. },
  435. full() {
  436. this.show = true;
  437. const width = document.documentElement.clientWidth;
  438. const height = document.documentElement.clientHeight;
  439. if (!this.isscreen) {
  440. console.log('全屏');
  441. this.isscreen = true;
  442. this.oheight = height;
  443. const transformid_ = document.getElementById('transformid');
  444. let style = 'width:' + height + 'px;';
  445. style += 'height:' + width + 'px;';
  446. style += '-webkit-transform: rotate(90deg); transform: rotate(90deg);';
  447. // 注意旋转中点的处理
  448. style += '-webkit-transform-origin: ' + width / 2 + 'px ' + width / 2 + 'px;';
  449. style += 'transform-origin: ' + width / 2 + 'px ' + width / 2 + 'px;';
  450. transformid_.style.cssText = style;
  451. this.transform = 'rotate(90deg)';
  452. // this.vwidth = height;
  453. console.log(height);
  454. this.vheight = `${width}px`;
  455. this.vwidth = `${height}px`;
  456. this.lheight = `${width}px`;
  457. this.rheight = `${width}px`;
  458. this.btop = `${height - 40}px`;
  459. this.bright = `${width - 50}px`;
  460. } else {
  461. console.log('退出全屏');
  462. this.isscreen = false;
  463. this.oheight = '200px';
  464. const transformid_ = document.getElementById('transformid');
  465. let style = 'width:' + width + 'px;';
  466. style += 'height: 200px;';
  467. transformid_.style.cssText = style;
  468. this.transform = 'rotate(0deg)';
  469. // this.vwidth = height;
  470. console.log(height);
  471. this.vheight = `200px`;
  472. this.vwidth = `${width}px`;
  473. this.lheight = `200px`;
  474. this.rheight = `200px`;
  475. this.btop = `167px`;
  476. this.bright = `10px`;
  477. }
  478. },
  479. },
  480. computed: {
  481. ...mapState(['user']),
  482. id() {
  483. return this.$route.query.id;
  484. },
  485. roomname() {
  486. return this.$route.query.roomname;
  487. },
  488. pageTitle() {
  489. return `${this.$route.meta.title}`;
  490. },
  491. },
  492. metaInfo() {
  493. return { title: this.$route.meta.title };
  494. },
  495. };
  496. </script>
  497. <style lang="less" scoped>
  498. .video {
  499. top: 0;
  500. left: 0;
  501. width: 100%;
  502. height: 200px;
  503. }
  504. .content {
  505. padding: 10px;
  506. }
  507. .info {
  508. .title {
  509. text-align: center;
  510. padding: 10px 0;
  511. font-size: 16px;
  512. font-weight: bold;
  513. }
  514. .num {
  515. padding: 10px 0;
  516. font-size: 16px;
  517. }
  518. .advert {
  519. background: #fff;
  520. padding: 10px;
  521. }
  522. .chat {
  523. min-height: 21px;
  524. max-height: 395px;
  525. overflow-y: auto;
  526. padding: 0 0 50px 0;
  527. .chatInfo {
  528. .list {
  529. margin: 10px 0 10px 0;
  530. span:first-child {
  531. float: left;
  532. width: 17%;
  533. text-align: center;
  534. overflow: hidden;
  535. text-overflow: ellipsis;
  536. white-space: nowrap;
  537. // font-weight: bold;
  538. }
  539. span:last-child {
  540. float: right;
  541. width: 82%;
  542. }
  543. }
  544. }
  545. }
  546. .submit {
  547. width: 100%;
  548. position: absolute;
  549. bottom: 0px;
  550. .el-button {
  551. width: 100%;
  552. padding: 20px 0;
  553. }
  554. }
  555. }
  556. .my-swipe .van-swipe-item {
  557. color: #fff;
  558. font-size: 20px;
  559. line-height: 120px;
  560. text-align: center;
  561. background-color: #39a9ed;
  562. }
  563. /deep/.video-js {
  564. height: 190px !important;
  565. border-radius: 10px;
  566. }
  567. .look-video-left {
  568. float: left;
  569. background: #000;
  570. grid-area: 1/1/3/4;
  571. justify-content: flex-end;
  572. }
  573. .look-video-right {
  574. float: right;
  575. background: #000;
  576. grid-area: 1/1/3/4;
  577. justify-content: flex-end;
  578. }
  579. .wrapper {
  580. background: #000;
  581. position: absolute;
  582. width: 100%;
  583. }
  584. .van-overlay {
  585. position: fixed;
  586. top: 0;
  587. left: 0;
  588. z-index: 1;
  589. width: 100%;
  590. height: 200px;
  591. background-color: rgba(0, 0, 0, 0.7);
  592. }
  593. .videoBtn {
  594. z-index: 999;
  595. position: absolute;
  596. }
  597. .userList {
  598. height: 380px;
  599. min-height: 380px;
  600. overflow-y: auto;
  601. padding: 0 10px;
  602. margin: 0 0 10px 0;
  603. }
  604. .questpopup {
  605. padding: 10px 10px;
  606. }
  607. .questpopup div {
  608. padding: 8px 0 0 5px;
  609. }
  610. .questtitle {
  611. padding: 8px 0 0 5px;
  612. text-align: center;
  613. font-size: 18px;
  614. font-weight: bold;
  615. }
  616. .questbtn {
  617. text-align: center;
  618. padding: 10px 0 15px 0;
  619. }
  620. /deep/.van-cell {
  621. padding: 25px 16px;
  622. font-size: 16px;
  623. }
  624. </style>