detailInfo.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. <template>
  2. <div id="detailInfo">
  3. <el-row>
  4. <el-col :span="24" class="info">
  5. <el-col :span="4" class="left">
  6. <el-col :span="24" class="leftTop">
  7. <el-image :src="roomInfo.filedir"></el-image>
  8. <p>{{ roomInfo.title }}</p>
  9. <p>{{ roomInfo.content }}</p>
  10. </el-col>
  11. <el-col :span="24" class="leftDown">
  12. <el-col :span="8" class="btn" @click.native="shexiangBtn()">
  13. <i class="iconfont iconshexiangtou"></i>
  14. <p>摄像头</p>
  15. </el-col>
  16. <el-col :span="8" class="btn" @click.native="tianchongBtn()">
  17. <i class="iconfont iconmaikefeng-tianchong"></i>
  18. <p>麦克风</p>
  19. </el-col>
  20. <el-col :span="8" class="btn" @click.native="chatBtn()">
  21. <i class="el-icon-user"></i>
  22. <p>聊天</p>
  23. </el-col>
  24. </el-col>
  25. </el-col>
  26. <el-col :span="20" class="right">
  27. <el-col :span="24" class="rightTop">
  28. <span @click="liveon"><i class="iconfont iconshexiangtou"></i>直播</span>
  29. <span @click="liveclose">关闭</span>
  30. <span @click="shareon"><i class="iconfont iconfenxiang"></i>屏幕</span>
  31. <span><el-switch @change="recordclick" v-model="isrecord" active-text="录制" inactive-text="停录"> </el-switch></span>
  32. </el-col>
  33. <el-col :span="2" class="noVideo"> </el-col>
  34. <el-col :span="20" class="video">
  35. <div id="main-video" class="video-box col-div" style="justify-content: flex-end"></div>
  36. </el-col>
  37. <el-col :span="2" class="noVideo"> </el-col>
  38. <el-col :span="24" class="rightDown">
  39. <!-- 开始直播 -->
  40. </el-col>
  41. </el-col>
  42. </el-col>
  43. </el-row>
  44. <el-dialog title="摄像头" :visible.sync="shexiangDia" width="30%" :before-close="handleClose">
  45. <el-select @change="cameraChange" v-model="cameraId" filterable placeholder="请选择摄像头">
  46. <el-option v-for="item in cameras" :key="item.deviceId" :label="item.label" :value="item.deviceId"> </el-option>
  47. </el-select>
  48. </el-dialog>
  49. <el-dialog title="麦克风" :visible.sync="tianchongDia" width="30%" :before-close="handleClose">
  50. <el-select @change="micrChange" v-model="microphoneId" filterable placeholder="请选择麦克风">
  51. <el-option v-for="item in microphones" :key="item.deviceId" :label="item.label" :value="item.deviceId"> </el-option>
  52. </el-select>
  53. </el-dialog>
  54. <el-dialog title="讨论" :visible.sync="chatDia" width="50%" :before-close="handleClose">
  55. <el-row>
  56. <el-col :span="24" class="chatList">
  57. <el-col :span="24" class="list" v-for="(item, index) in dataList" :key="index">
  58. <p>
  59. <span :class="item.sendname == user.name ? 'selfColor' : ''">{{ item.sendname }}</span>
  60. <span>{{ item.content }}</span>
  61. </p>
  62. </el-col>
  63. </el-col>
  64. <el-col :span="24" class="chatInput">
  65. <el-col :span="19" class="input">
  66. <el-input type="textarea" maxlength="5000" show-word-limit v-model="content"></el-input>
  67. </el-col>
  68. <el-col :span="5" class="btn">
  69. <el-button type="primary" size="mini" @click="chatCreate">发送</el-button>
  70. </el-col>
  71. </el-col>
  72. </el-row>
  73. </el-dialog>
  74. </div>
  75. </template>
  76. <script>
  77. import Vue from 'vue';
  78. import { mapState, createNamespacedHelpers } from 'vuex';
  79. const { mapActions: gensign } = createNamespacedHelpers('gensign');
  80. const { mapActions: chat } = createNamespacedHelpers('chat');
  81. const { mapActions: room } = createNamespacedHelpers('room');
  82. import TRTC from 'trtc-js-sdk';
  83. export default {
  84. name: 'detailInfo',
  85. props: {
  86. roomInfo: null,
  87. },
  88. components: {},
  89. data: function() {
  90. return {
  91. // 摄像头
  92. shexiangDia: false,
  93. cameraId: '',
  94. cameras: [],
  95. // 麦克风
  96. tianchongDia: false,
  97. chatDia: false,
  98. microphoneId: '',
  99. microphones: [],
  100. client_: '',
  101. localStream_: '',
  102. sdkAppId_: '1400380125',
  103. userId_: '1111',
  104. open_: false,
  105. content: '',
  106. dataList: [],
  107. isrecord: false,
  108. };
  109. },
  110. created() {
  111. this.initclient();
  112. this.getDevices();
  113. this.chatSearch();
  114. },
  115. mounted() {
  116. this.channel();
  117. },
  118. methods: {
  119. ...gensign(['gensignFetch']),
  120. ...chat(['query', 'create', 'fetch']),
  121. ...room(['startrecord', 'stoprecord']),
  122. async recordclick() {
  123. console.log(this.isrecord);
  124. if (this.isrecord) {
  125. const info = { roomid: this.id, roomname: this.name };
  126. let res = await this.startrecord({ ...info });
  127. } else {
  128. const info = { roomid: this.id, roomname: this.name };
  129. let res = await this.stoprecord({ ...info });
  130. }
  131. },
  132. async chatSearch({ skip = 0, limit = 1000 } = {}) {
  133. const info = { roomid: this.id };
  134. let res = await this.query({ skip, limit, ...info });
  135. this.$set(this, `dataList`, res.data);
  136. },
  137. async chatCreate() {
  138. let data = {};
  139. data.roomid = this.id;
  140. data.type = '0';
  141. data.content = this.content;
  142. data.sendid = this.user.uid;
  143. data.sendname = this.user.name;
  144. const res = await this.create(data);
  145. if (this.$checkRes(res)) {
  146. console.log(res.data);
  147. this.content = '';
  148. }
  149. },
  150. channel() {
  151. console.log('in function:');
  152. this.$stomp({
  153. [`/exchange/public_chat_` + this.id]: this.onMessage,
  154. });
  155. },
  156. onMessage(message) {
  157. // console.log('receive a message: ', message.body);
  158. let body = _.get(message, 'body');
  159. if (body) {
  160. body = JSON.parse(body);
  161. this.dataList.push(body);
  162. this.content = '';
  163. }
  164. // const { content, contenttype, sendid, sendname, icon, groupid, sendtime, type } = message.headers;
  165. // let object = { content, contenttype, sendid, sendname, icon, groupid, sendtime, type };
  166. // this.list.push(object);
  167. },
  168. async getDevices() {
  169. this.cameras = await TRTC.getCameras();
  170. this.microphones = await TRTC.getMicrophones();
  171. },
  172. async initclient() {
  173. console.log(this.user.uid);
  174. this.userId_ = this.user.uid;
  175. const res = await this.gensignFetch({ userid: this.userId_ });
  176. if (this.$checkRes(res)) {
  177. console.log(res.data);
  178. this.client_ = TRTC.createClient({
  179. mode: 'live',
  180. sdkAppId: this.sdkAppId_,
  181. userId: this.userId_,
  182. userSig: res.data,
  183. });
  184. }
  185. },
  186. async liveon() {
  187. this.open_ = true;
  188. console.log('8888--' + this.userId_);
  189. if (this.cameraId === '' || this.microphoneId === '') {
  190. this.$message({
  191. message: '请选择摄像头和麦克风',
  192. type: 'warning',
  193. });
  194. return;
  195. }
  196. await this.client_.join({ roomId: this.name, role: 'anchor' });
  197. this.localStream_ = await TRTC.createStream({
  198. audio: true,
  199. video: true,
  200. cameraId: this.cameraId,
  201. microphoneId: this.microphoneId,
  202. userId: this.userId_,
  203. });
  204. this.localStream_.setVideoProfile('480p');
  205. await this.localStream_.initialize();
  206. console.log('initialize local stream success');
  207. // publish the local stream
  208. await this.client_.publish(this.localStream_);
  209. this.localStream_.play('main-video');
  210. //$('#mask_main').appendTo($('#player_' + this.localStream_.getId()));
  211. },
  212. async shareon() {
  213. const shareId = 'share-' + this.userId_;
  214. const res = await this.gensignFetch({ userid: shareId });
  215. if (this.$checkRes(res)) {
  216. const shareClient = TRTC.createClient({
  217. mode: 'videoCall',
  218. sdkAppId: this.sdkAppId_,
  219. userId: shareId,
  220. userSig: res.data,
  221. });
  222. shareClient.setDefaultMuteRemoteStreams(true);
  223. await shareClient.join({ roomId: this.name });
  224. const localStream = TRTC.createStream({ audio: false, screen: true });
  225. //localStream.setScreenProfile({ width: 200, height: 200, float: 'left', frameRate: 5, bitrate: 1600 /* kbps */ });
  226. await localStream.initialize();
  227. console.log('initialize share stream success');
  228. await shareClient.publish(localStream);
  229. this.client_.on('stream-added', event => {
  230. const remoteStream = event.stream;
  231. const remoteUserId = remoteStream.getUserId();
  232. if (remoteUserId === shareId) {
  233. // 取消订阅自己的屏幕分享流
  234. this.client_.unsubscribe(remoteStream);
  235. } else {
  236. // 订阅其他一般远端流
  237. this.client_.subscribe(remoteStream);
  238. }
  239. });
  240. }
  241. },
  242. async liveclose() {
  243. // 关闭视频通话
  244. console.log(this.open_);
  245. if (this.open_) {
  246. const videoTrack = this.localStream_.getVideoTrack();
  247. if (videoTrack) {
  248. this.localStream_.removeTrack(videoTrack).then(() => {
  249. console.log('remove video call success');
  250. // 关闭摄像头
  251. videoTrack.stop();
  252. this.client_.unpublish(this.localStream_).then(() => {
  253. // 取消发布本地流成功
  254. });
  255. });
  256. }
  257. }
  258. },
  259. async cameraChange() {
  260. await this.localStream_.switchDevice('video', this.cameraId);
  261. },
  262. async micrChange() {
  263. await this.localStream_.switchDevice('audio', this.microphoneId);
  264. },
  265. // 选择打开摄像头
  266. shexiangBtn() {
  267. this.shexiangDia = true;
  268. },
  269. // 选择打开麦克风
  270. tianchongBtn() {
  271. this.tianchongDia = true;
  272. },
  273. chatBtn() {
  274. this.chatDia = true;
  275. },
  276. // 关闭摄像头&麦克风
  277. handleClose(done) {
  278. done();
  279. },
  280. },
  281. computed: {
  282. ...mapState(['user']),
  283. id() {
  284. return this.$route.query.id;
  285. },
  286. name() {
  287. return this.$route.query.name;
  288. },
  289. pageTitle() {
  290. return `${this.$route.meta.title}`;
  291. },
  292. },
  293. metaInfo() {
  294. return { title: this.$route.meta.title };
  295. },
  296. };
  297. </script>
  298. <style lang="less" scoped>
  299. .info {
  300. background-color: #2a2b30;
  301. min-height: 840px;
  302. .left {
  303. .leftTop {
  304. min-height: 540px;
  305. padding: 0 15px;
  306. margin: 20px 0;
  307. p {
  308. color: #ccc;
  309. }
  310. p:nth-child(2) {
  311. padding: 10px 0;
  312. }
  313. p:nth-child(3) {
  314. line-height: 25px;
  315. }
  316. }
  317. .leftDown {
  318. padding: 10px 0 0 0;
  319. border-top: 1px solid #000;
  320. .btn {
  321. margin: 0 0 15px 0;
  322. color: #cccccc;
  323. text-align: center;
  324. }
  325. .btn:hover {
  326. cursor: pointer;
  327. }
  328. }
  329. }
  330. .right {
  331. .rightTop {
  332. height: 100px;
  333. background-color: #232428;
  334. text-align: right;
  335. color: #ccc;
  336. line-height: 100px;
  337. span {
  338. margin: 0 10px 0 0;
  339. }
  340. span:hover {
  341. cursor: pointer;
  342. color: #409eff;
  343. }
  344. }
  345. .video {
  346. min-height: 640px;
  347. background-color: #000;
  348. }
  349. .noVideo {
  350. min-height: 640px;
  351. background-color: #151618;
  352. }
  353. .rightDown {
  354. height: 100px;
  355. background-color: #232428;
  356. }
  357. }
  358. }
  359. /deep/.el-dialog__body {
  360. min-height: 100px;
  361. }
  362. #main-video {
  363. float: left;
  364. width: 100%;
  365. height: 640px;
  366. min-height: 600px;
  367. grid-area: 1/1/3/4;
  368. }
  369. .chatList {
  370. height: 400px;
  371. padding: 5px 5px 5px 0px;
  372. overflow-y: auto;
  373. .list {
  374. margin: 0 0 10px 0;
  375. span:first-child {
  376. float: left;
  377. width: 15%;
  378. text-align: center;
  379. overflow: hidden;
  380. text-overflow: ellipsis;
  381. white-space: nowrap;
  382. // font-weight: bold;
  383. }
  384. span:last-child {
  385. float: right;
  386. width: 84%;
  387. }
  388. }
  389. }
  390. .chatInput {
  391. position: absolute;
  392. bottom: 0;
  393. .el-button {
  394. width: 100%;
  395. padding: 20px 0;
  396. }
  397. }
  398. </style>