index.vue 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. <template>
  2. <div class="live">
  3. <!--顶部-->
  4. <div class="live-top">
  5. <div class="live-title">
  6. <div
  7. class="live-title-name nowrap-ellipsis"
  8. :title="`${roomInfo.course_name} - ${roomInfo.cs_item_name} - ${roomInfo.task_name}`"
  9. >
  10. {{ roomInfo.course_name }} - {{ roomInfo.cs_item_name }} - {{ roomInfo.task_name }}
  11. </div>
  12. <div class="live-button-group">
  13. <el-button v-if="!liveStat" @click="startLive">
  14. {{ $t('Key418') }}
  15. </el-button>
  16. <el-button v-if="liveStat" icon="el-icon-switch-button" @click="closeLiveRoom">
  17. {{ $t('Key419') }}
  18. </el-button>
  19. <el-button v-if="liveStat" @click="reconnection">
  20. {{ $t('Key405') }}
  21. </el-button>
  22. <el-button @click="setDevice(true)">
  23. {{ $t('Key398') }}
  24. </el-button>
  25. </div>
  26. </div>
  27. </div>
  28. <!-- 主容器 -->
  29. <div class="live-container">
  30. <!-- 左侧 -->
  31. <div class="live-container-left">
  32. <div v-show="callLoading" class="loading">
  33. <div class="loading-wrapper">
  34. <el-avatar icon="el-icon-user" :src="connectStudent.student_image_url" />
  35. <p class="loading-title">
  36. {{ $t('Key420') }}【{{ connectStudent.student_name }}】,{{ $t('Key421') }}...
  37. </p>
  38. <div>
  39. <el-button type="danger" circle @click="handsDown">
  40. <svg-icon icon-class="hang-up" />
  41. </el-button>
  42. </div>
  43. </div>
  44. </div>
  45. <div v-show="connect" class="student-parent">
  46. <div v-show="roomInfo.video_mode === 1 || remoteStreamType === 1" id="student" />
  47. <template v-if="remoteStreamType !== 1">
  48. <template v-if="roomInfo.video_mode === 1">
  49. <el-button type="danger" @click="handsDown">
  50. <svg-icon icon-class="hang-up" /> {{ $t('Key422') }}
  51. </el-button>
  52. </template>
  53. <template v-else>
  54. <div class="student-audio">
  55. <el-avatar icon="el-icon-user" :src="connectStudent.student_image_url" />
  56. <span class="connect-name">{{ connectStudent.student_name }}</span>
  57. <el-button type="danger" circle @click="handsDown">
  58. <svg-icon icon-class="hang-up" />
  59. </el-button>
  60. </div>
  61. </template>
  62. </template>
  63. </div>
  64. <div v-show="isDraw" id="draw-parent">
  65. <!-- 画笔设置 -->
  66. <div v-show="isDrawSetting" class="draw-setting">
  67. <span class="brush-shape" @click="drawChange('type', 2)">
  68. <svg-icon icon-class="brush-shape" />
  69. </span>
  70. <!-- 画笔颜色 -->
  71. <span
  72. v-for="item in drawColorList"
  73. :key="item"
  74. :class="['draw-color', item === curColor ? 'current' : '']"
  75. :style="{ 'background-color': item }"
  76. @click="drawChange('color', item)"
  77. />
  78. <span
  79. v-for="item in drawThicknessList"
  80. :key="item"
  81. class="draw-thickness"
  82. @click="drawChange('thickNess', item)"
  83. >
  84. <span :style="{ width: item * 2 + 'px', height: item * 2 + 'px' }" />
  85. </span>
  86. <span class="eraser" @click="drawChange('type', 9)">
  87. <svg-icon icon-class="back" />
  88. </span>
  89. <span class="eraser" @click="drawChange('type', 10)">
  90. <svg-icon icon-class="eraser" />
  91. </span>
  92. <span class="brush-clear" @click="drawChange('type', 0)">
  93. <svg-icon icon-class="clear" />
  94. </span>
  95. </div>
  96. </div>
  97. <div class="button-group">
  98. <div class="button-group-left">
  99. <span class="icon-button" @click="publishShareStream">
  100. <svg-icon :icon-class="remoteStreamType === 1 ? 'close' : 'share'" />
  101. </span>
  102. <span class="icon-button" @click="showDrawSetting">
  103. <svg-icon icon-class="draw" />
  104. </span>
  105. <span class="icon-button" @click="startGroup">
  106. <svg-icon icon-class="group" />
  107. </span>
  108. <span class="icon-button" @click="dialogVisible = true">
  109. <svg-icon icon-class="push" />
  110. </span>
  111. </div>
  112. <div class="button-group-right" />
  113. </div>
  114. </div>
  115. <!-- 右侧 -->
  116. <div class="live-container-right">
  117. <div class="live-teacher-lens">
  118. <div id="live" @mouseover="liveMenuShow = true" @mouseout="liveMenuShow = false" />
  119. <div :style="{ bottom: liveMenuShow ? '0' : '-40px' }" class="live-wrapper">
  120. <div class="live-wrapper-right">
  121. <span :style="{ color: netStatusColor }">{{ netStatus }}</span>
  122. </div>
  123. </div>
  124. <div class="live-operate">
  125. <div>{{ roomInfo.teacher_name }}</div>
  126. <div class="live-operate-icon">
  127. <svg-icon :icon-class="hasVideo ? 'camera-on-black' : 'camera-off-black'" @click="playOrPauseVideo" />
  128. <svg-icon :icon-class="hasAudio ? 'mike-on-black' : 'mike-off-black'" @click="playOrPauseAudio" />
  129. </div>
  130. </div>
  131. </div>
  132. <div class="live-container-right-chat">
  133. <div class="chat-top">
  134. <span>{{ $t('Key409') }}</span>
  135. <span class="chat-bans" @click="chatBans">
  136. <input type="checkbox" :checked="isAllowChat" class="allow-chat">
  137. <span>{{ $t('Key423') }}</span>
  138. </span>
  139. </div>
  140. <div class="chat-window">
  141. <ul ref="chat" class="chat-window-ul">
  142. <li v-for="(item, i) in chatList" :key="i">
  143. <div class="msg-normal">
  144. <span>{{ item.username }}: </span>
  145. <span>{{ item.msg }}</span>
  146. </div>
  147. </li>
  148. </ul>
  149. </div>
  150. <div class="chat-speak">
  151. <el-input v-model="msg" :placeholder="$t('Key410')" maxlength="400" @keydown.enter.native="sendMsg">
  152. <el-button slot="append" @click="sendMsg">
  153. {{ $t('Key302') }}
  154. </el-button>
  155. </el-input>
  156. </div>
  157. </div>
  158. </div>
  159. </div>
  160. <!-- 推送资料列表 -->
  161. <div class="material-list">
  162. <el-tag
  163. v-for="item in material_list"
  164. :key="item.material_id"
  165. color="#fff"
  166. @click="showMaterial(item.material_id, item.material_type)"
  167. >
  168. <svg-icon :icon-class="item.material_type === 'COURSEWARE' ? 'courseware' : 'word'" /> {{ item.material_name }}
  169. </el-tag>
  170. </div>
  171. <!-- 学员列表 -->
  172. <div class="student-list">
  173. <div class="student-list-title">
  174. <span>{{ $t('Key296') }}({{ student_list.length }} / {{ roomInfo.student_count }})</span>
  175. </div>
  176. <div class="student-list-container">
  177. <ul>
  178. <li v-for="item in student_list" :key="item.room_user_id">
  179. <div class="li-top">
  180. <svg-icon
  181. v-if="item.is_mobile === 'true'"
  182. :icon-class="item.is_exit_page === 'true' ? 'mobile-close' : 'mobile'"
  183. />
  184. <el-avatar icon="el-icon-user" :size="40" :src="item.student_image_url" />
  185. </div>
  186. <div class="li-bottom">
  187. <div class="name">
  188. {{ item.student_name }}
  189. </div>
  190. <div class="li-bottom-operate">
  191. <svg-icon
  192. v-if="item.connection_mode === 1 && item.connection_status !== 0"
  193. icon-class="hang-up-black"
  194. @click="handsDown(item.room_user_id)"
  195. />
  196. <svg-icon
  197. v-else
  198. icon-class="video"
  199. @click="invite(item, 1, item.is_mobile === 'true', item.is_exit_page === 'true')"
  200. />
  201. <svg-icon
  202. v-if="item.connection_mode === 2 && item.connection_status !== 0"
  203. icon-class="hang-up-black"
  204. @click="handsDown(item.room_user_id)"
  205. />
  206. <svg-icon
  207. v-else
  208. icon-class="voice"
  209. @click="invite(item, 2, item.is_mobile === 'true', item.is_exit_page === 'true')"
  210. />
  211. </div>
  212. </div>
  213. </li>
  214. </ul>
  215. </div>
  216. </div>
  217. <!-- 推送资料 -->
  218. <select-material
  219. :dialog-visible="dialogVisible"
  220. :task-id="task_id"
  221. @dialogClose="dialogClose"
  222. @dialogPush="dialogPush"
  223. />
  224. <!-- 教师查看当前完成列表 -->
  225. <complete-list
  226. :task-id="task_id"
  227. :material-id="materialId"
  228. :material-type="materialType"
  229. :dialog-visible-complete="dialogVisibleComplete"
  230. @dialogCompleteClose="dialogCompleteClose"
  231. />
  232. <select-device
  233. :dialog-visible-device="dialogVisibleDevice"
  234. :device="device"
  235. @dialogDeviceClose="dialogDeviceClose"
  236. />
  237. <el-dialog
  238. :title="$t('Key424')"
  239. top="30vh"
  240. width="300px"
  241. class="dialog-group"
  242. :visible="dialogVisibleGroup"
  243. :close-on-click-modal="false"
  244. >
  245. <el-select v-model="group_count">
  246. <el-option v-for="i in groupNumList" :key="i" :label="i" :value="i" />
  247. </el-select>
  248. <span slot="footer">
  249. <el-button size="small" @click="closeGroup">{{ $t('Key83') }}</el-button>
  250. <el-button size="small" type="primary" @click="dialogGroup">{{ $t('Key94') }}</el-button>
  251. </span>
  252. </el-dialog>
  253. </div>
  254. </template>
  255. <script>
  256. import {
  257. GetLiveRoomData_DRTD,
  258. CloseLiveRoom,
  259. GetLiveRoomInfo,
  260. StudentExitLiveRoom,
  261. StartGroup,
  262. GetGroupStatus,
  263. DealStudentConnection,
  264. GetStudentInfo_Connection
  265. } from '@/api/live';
  266. import { app } from '@/store/mutation-types';
  267. import SelectMaterial from '@/components/live/SelectMaterial.vue';
  268. import CompleteList from './CompleteList.vue';
  269. import SelectDevice from '../SelectDevice.vue';
  270. import * as common from './live';
  271. export default {
  272. components: {
  273. SelectMaterial,
  274. CompleteList,
  275. SelectDevice
  276. },
  277. data() {
  278. return {
  279. task_id: this.$route.query.task_id,
  280. // 连麦
  281. connect: false,
  282. // 连线学员信息
  283. connectStudent: {},
  284. // 等待接通
  285. callLoading: false,
  286. dialogVisible: false,
  287. // 学员完成
  288. dialogVisibleComplete: false,
  289. // 定时器
  290. timer: null,
  291. remoteStreamType: -1,
  292. rtc: null,
  293. roomData: {
  294. desc: '直播间标题',
  295. name: '姓名',
  296. user: {
  297. id: '',
  298. name: '',
  299. role: 'talker',
  300. rommid: ''
  301. },
  302. max_users: 1,
  303. allow_chat: true,
  304. allow_audio: true,
  305. allow_speak: true
  306. },
  307. roomInfo: {
  308. room_id: '',
  309. video_mode: 1,
  310. task_name: '',
  311. cs_item_name: '',
  312. course_name: '',
  313. teacher_name: '',
  314. student_count: 0,
  315. student_connection_info: {}
  316. },
  317. loadedNumber: 0,
  318. speakData: {},
  319. roomContext: {},
  320. msg: '',
  321. chatList: [],
  322. isDrawSetting: false,
  323. curColor: '#343434',
  324. drawColorList: ['#FF4747', '#343434', '#628EFF', '#FFCA0E'],
  325. drawThicknessList: ['1', '3', '5'],
  326. // 直播间学员列表
  327. student_list: [],
  328. // 直播状态
  329. liveStat: false,
  330. liveMenuShow: false,
  331. // 分组讨论
  332. groupNumList: [],
  333. group_count: 1,
  334. // 网络整体情况
  335. netStatus: 0,
  336. dialogVisibleGroup: false,
  337. dialogVisibleDevice: false,
  338. isRecreate: false,
  339. device: {
  340. video: [],
  341. audio: []
  342. },
  343. // 本地视频流画面、声音
  344. hasVideo: false,
  345. hasAudio: false,
  346. material_list: [],
  347. materialId: '',
  348. materialType: ''
  349. };
  350. },
  351. computed: {
  352. // 画板模式
  353. isDraw() {
  354. return !this.connect && !this.callLoading;
  355. },
  356. isAllowChat() {
  357. return !this.roomData.allow_chat;
  358. },
  359. netStatusColor() {
  360. if (this.netStatus >= 1000) {
  361. return '#f00';
  362. }
  363. if (this.netStatus >= 500) {
  364. return '#d6e91a';
  365. }
  366. if (this.netStatus >= 200) {
  367. return '#6aee4c';
  368. }
  369. return '#38d514';
  370. },
  371. connectUid() {
  372. return 'room_user_id' in this.connectStudent ? this.connectStudent.room_user_id : '';
  373. }
  374. },
  375. watch: {
  376. loadedNumber(newVal) {
  377. if (newVal === 5) {
  378. common.createScript('https://class.csslcloud.net/static/SDK/docSDK/drawSdk_3.0.js').onload = () => {
  379. this.initSDK();
  380. this.$loading().close();
  381. };
  382. }
  383. },
  384. // 聊天列表滚动
  385. chatList() {
  386. common.chatRoll(this, false);
  387. }
  388. },
  389. created() {
  390. GetGroupStatus({ task_id: this.task_id }).then(({ is_enable_group }) => {
  391. if (is_enable_group === 'true') {
  392. this.$router.push({
  393. path: '/live/teacher/group',
  394. query: {
  395. task_id: this.task_id
  396. }
  397. });
  398. } else {
  399. this.$loading({
  400. text: this.$i18n.t('Key425'),
  401. background: '#fff'
  402. });
  403. common.downloadWebSDK(this);
  404. this.getLiveRoomData_DRTD();
  405. this.getLiveRoomInfo();
  406. }
  407. });
  408. this.updateWordPack({
  409. word_key_list: [
  410. 'Key418',
  411. 'Key419',
  412. 'Key405',
  413. 'Key398',
  414. 'Key420',
  415. 'Key421',
  416. 'Key422',
  417. 'Key409',
  418. 'Key423',
  419. 'Key410',
  420. 'Key302',
  421. 'Key296',
  422. 'Key424',
  423. 'Key94',
  424. 'Key83',
  425. 'Key425',
  426. 'Key426',
  427. 'Key427',
  428. 'Key428',
  429. 'Key429',
  430. 'Key430',
  431. 'Key431',
  432. 'Key432',
  433. 'Key433',
  434. 'Key434',
  435. 'Key435',
  436. 'Key436',
  437. 'Key437',
  438. 'Key438',
  439. 'Key399',
  440. 'Key401',
  441. 'Key441'
  442. ]
  443. });
  444. },
  445. mounted() {
  446. document.addEventListener(
  447. 'click',
  448. e => {
  449. let target = e.target;
  450. let isHasClass = false;
  451. do {
  452. if (target === null) {
  453. break;
  454. }
  455. if (target.className === 'draw-setting') {
  456. isHasClass = true;
  457. }
  458. target = target.parentElement;
  459. } while (!isHasClass);
  460. if (!isHasClass) {
  461. this.isDrawSetting = false;
  462. }
  463. },
  464. true
  465. );
  466. this.getLiveRoomData_DRTDPolling();
  467. },
  468. beforeDestroy() {
  469. clearInterval(this.timer);
  470. common.closeVideo('main');
  471. },
  472. methods: {
  473. initSDK() {
  474. const { live_room_sys_user_id, room_id, session_id } = this.$route.query;
  475. this.rtc = common.initSDK({
  476. userid: live_room_sys_user_id,
  477. roomid: room_id,
  478. sessionid: session_id
  479. });
  480. common.initListener(this); // 注册监听事件
  481. this.getLiveStat();
  482. },
  483. getLiveRoomInfo() {
  484. GetLiveRoomInfo({ task_id: this.task_id }).then(
  485. ({
  486. room_id,
  487. video_mode,
  488. task_name,
  489. cs_item_name,
  490. course_name,
  491. teacher_name,
  492. student_count,
  493. student_connection_info
  494. }) => {
  495. this.roomInfo = {
  496. room_id,
  497. video_mode,
  498. task_name,
  499. cs_item_name,
  500. course_name,
  501. teacher_name,
  502. student_count,
  503. student_connection_info
  504. };
  505. this.connectStudent = student_connection_info;
  506. if (student_connection_info.connection_status === 1) {
  507. this.callLoading = true;
  508. }
  509. if (student_connection_info.connection_status === 2) {
  510. this.connect = true;
  511. }
  512. }
  513. );
  514. },
  515. closeLiveRoom() {
  516. CloseLiveRoom({ task_id: this.task_id }).then(() => {
  517. this.$router.push('/');
  518. this.$message.success(this.$i18n.t('Key426'));
  519. });
  520. },
  521. startLive() {
  522. common.startLive();
  523. },
  524. getLiveStat() {
  525. common.getLiveStat({
  526. success: data => {
  527. this.liveStat = data.started;
  528. },
  529. fail: str => {
  530. this.liveStat = false;
  531. console.log('直播关闭状态或查询直播失败', str);
  532. }
  533. });
  534. },
  535. // 推送桌面共享
  536. publishShareStream() {
  537. if (this.remoteStreamType === 1) {
  538. common.unPubShareStream();
  539. } else {
  540. common.publishShareStream();
  541. }
  542. },
  543. reconnection() {
  544. common.reconnection();
  545. },
  546. // 老师邀请学生上麦
  547. invite(student, mode, is_mobile, is_exit_page) {
  548. if (is_mobile && is_exit_page) {
  549. this.$message.warning(this.$i18n.t('Key427'));
  550. return;
  551. }
  552. if (this.connect || this.callLoading) {
  553. this.$message.warning(this.$i18n.t('Key428'));
  554. return;
  555. }
  556. this.callLoading = true;
  557. this.connectStudent = student;
  558. GetLiveRoomInfo({ task_id: this.task_id })
  559. .then(({ video_mode }) => {
  560. let uid = student.room_user_id;
  561. this.roomInfo.video_mode = mode;
  562. if (video_mode === mode) {
  563. this.inviteStudent(uid, student, mode);
  564. } else {
  565. common.roomUpdate({
  566. video_mode: mode,
  567. roomUpdateSuccess: data => {
  568. console.log(data, '连麦音视频模式更新请求成功!');
  569. this.inviteStudent(uid, student, mode);
  570. },
  571. roomUpdateFailed: () => {
  572. this.callLoading = false;
  573. this.$message.error(this.$i18n.t('Key429'));
  574. }
  575. });
  576. }
  577. })
  578. .catch(() => {
  579. this.callLoading = false;
  580. });
  581. },
  582. inviteStudent(uid, connectStudent, mode) {
  583. common.invite({
  584. uid,
  585. success: str => {
  586. console.log('邀请上麦成功', str);
  587. this.dealStudentConnection(uid, 1, mode);
  588. common.sendPublishMessage({
  589. type: 'inviteImage',
  590. connectStudent
  591. });
  592. },
  593. fail: data => {
  594. console.log('邀请上麦失败:', data);
  595. this.callLoading = false;
  596. this.$message.error(`${this.$i18n.t('Key430')}:${data.errorMsg}`);
  597. }
  598. });
  599. },
  600. dealStudentConnection(room_user_id, deal_mode, connection_mode) {
  601. DealStudentConnection({
  602. task_id: this.task_id,
  603. room_user_id,
  604. deal_mode,
  605. connection_mode
  606. }).then(() => {
  607. this.getLiveRoomData_DRTD();
  608. });
  609. },
  610. handsDown(uid) {
  611. let connectUid = typeof uid === 'string' ? uid : this.connectUid;
  612. if (connectUid.length === 0) {
  613. GetStudentInfo_Connection({ task_id: this.task_id }).then(({ room_user_id }) =>
  614. this.handsDown_Live(room_user_id)
  615. );
  616. } else {
  617. this.handsDown_Live(connectUid);
  618. }
  619. },
  620. // 下麦
  621. handsDown_Live(uid) {
  622. common.handsDown({
  623. uid,
  624. success: () => {
  625. if (this.callLoading) {
  626. common.sendPublishMessage({
  627. type: 'handsDown-load',
  628. uid
  629. });
  630. }
  631. this.dealStudentConnection(uid, 0, this.connectStudent.connection_mode);
  632. this.callLoading = false;
  633. this.connect = false;
  634. common.updateMcResult('', 0);
  635. this.$message.success(this.$i18n.t('Key431'));
  636. this.connectStudent = {};
  637. },
  638. fail: data => {
  639. this.callLoading = false;
  640. this.connect = false;
  641. console.log('下麦失败', data);
  642. this.$message.warning(`${this.$i18n.t('Key432')}:${data.errorMsg}`);
  643. }
  644. });
  645. },
  646. // 本地流视频开启、关闭
  647. playOrPauseVideo() {
  648. if (this.device.video.length === 0) {
  649. return this.$message.warning(this.$i18n.t('Key399'));
  650. }
  651. if (this.hasVideo) {
  652. common.pauseVideo({
  653. streamName: 'main',
  654. success: () => {
  655. this.$message.success(this.$i18n.t('Key433'));
  656. this.hasVideo = false;
  657. },
  658. fail: str => {
  659. this.$message.warning(str);
  660. }
  661. });
  662. } else {
  663. common.playVideo({
  664. streamName: 'main',
  665. success: () => {
  666. this.$message.success(this.$i18n.t('Key434'));
  667. this.hasVideo = true;
  668. },
  669. fail: str => {
  670. this.$message.warning(str);
  671. }
  672. });
  673. }
  674. },
  675. // 本地流音频开启、关闭
  676. playOrPauseAudio() {
  677. if (this.device.audio.length === 0) {
  678. return this.$message.warning(this.$i18n.t('Key401'));
  679. }
  680. if (this.hasAudio) {
  681. common.pauseAudio({
  682. streamName: 'main',
  683. success: () => {
  684. this.$message.success(this.$i18n.t('Key435'));
  685. this.hasAudio = false;
  686. },
  687. fail: str => {
  688. this.$message.warning(str);
  689. }
  690. });
  691. } else {
  692. common.playAudio({
  693. streamName: 'main',
  694. success: () => {
  695. this.$message.success(this.$i18n.t('Key436'));
  696. this.hasAudio = true;
  697. },
  698. fail: str => {
  699. this.$message.warning(str);
  700. }
  701. });
  702. }
  703. },
  704. // 发消息
  705. sendMsg() {
  706. common.sendMsg(this.msg);
  707. this.msg = '';
  708. },
  709. chatBans() {
  710. common.roomUpdate({
  711. allow_chat: !this.roomData.allow_chat,
  712. roomUpdateSuccess: data => {
  713. this.roomData.allow_chat = !this.roomData.allow_chat;
  714. console.log(data, '房间模板配置更新请求成功!');
  715. },
  716. roomUpdateFailed(data) {
  717. console.log(data, '房间模板配置更新请求失败! 请稍后再试!');
  718. }
  719. });
  720. },
  721. // 画笔变更
  722. drawChange(action, value) {
  723. if (action === 'color') {
  724. common.drawChange(action, parseInt(Number(value.replace('#', '0x'))));
  725. this.curColor = value;
  726. } else {
  727. common.drawChange(action, value);
  728. }
  729. this.isDrawSetting = false;
  730. },
  731. // 设置音视频设备
  732. setDevice(isRecreate) {
  733. this.isRecreate = isRecreate;
  734. this.dialogVisibleDevice = true;
  735. },
  736. getNetPoint() {
  737. common.getNetPoint();
  738. },
  739. dialogDeviceClose(device) {
  740. this.$store.commit(`app/${app.SET_DEVICE}`, device);
  741. this.dialogVisibleDevice = false;
  742. if (this.isRecreate) {
  743. common.closeVideoTeacher({
  744. streamName: 'main',
  745. success: () => {
  746. common.createLocalStream(this);
  747. },
  748. fail: str => {
  749. console.log(str);
  750. }
  751. });
  752. } else {
  753. common.createLocalStream(this);
  754. }
  755. },
  756. showDrawSetting() {
  757. this.isDrawSetting = !this.isDrawSetting;
  758. },
  759. getLiveRoomData_DRTD() {
  760. GetLiveRoomData_DRTD({ task_id: this.task_id }).then(({ student_list, material_list }) => {
  761. this.student_list = student_list;
  762. this.material_list = material_list;
  763. });
  764. },
  765. getLiveRoomData_DRTDPolling() {
  766. this.timer = setInterval(() => {
  767. this.getLiveRoomData_DRTD();
  768. }, 5000);
  769. },
  770. studentExitLiveRoom(room_user_id, is_exit_page = true) {
  771. StudentExitLiveRoom({ task_id: this.task_id, room_user_id, is_exit_page });
  772. },
  773. publishStream() {
  774. common.publishStream('main');
  775. },
  776. // 分组讨论
  777. startGroup() {
  778. for (let i = 1; i <= this.student_list.length; i++) {
  779. this.groupNumList.push(i);
  780. }
  781. this.dialogVisibleGroup = true;
  782. },
  783. showMaterial(id, type) {
  784. this.materialId = id;
  785. this.materialType = type;
  786. this.dialogVisibleComplete = true;
  787. },
  788. // 弹出框方法
  789. dialogGroup() {
  790. const loading = this.$loading({
  791. text: this.$i18n.t('Key437')
  792. });
  793. // 开始分组讨论
  794. StartGroup({ task_id: this.task_id, group_count: this.group_count })
  795. .then(() => {
  796. this.$message.success(this.$i18n.t('Key438'));
  797. this.$router.push({
  798. path: '/live/teacher/group',
  799. query: {
  800. task_id: this.task_id
  801. }
  802. });
  803. })
  804. .finally(() => {
  805. loading.close();
  806. });
  807. },
  808. closeGroup() {
  809. this.dialogVisibleGroup = false;
  810. this.groupNumList = [];
  811. this.group_count = 1;
  812. },
  813. dialogClose() {
  814. this.dialogVisible = false;
  815. },
  816. dialogPush() {
  817. this.dialogVisible = false;
  818. this.dialogVisibleComplete = true;
  819. },
  820. dialogCompleteClose() {
  821. this.dialogVisibleComplete = false;
  822. this.materialId = '';
  823. this.materialType = '';
  824. }
  825. }
  826. };
  827. </script>
  828. <style lang="scss" scoped>
  829. @import '~@/styles/mixin';
  830. $live-bc: #3d3938;
  831. $draw-h: 520px;
  832. .live {
  833. @include dialog;
  834. min-width: 1440px;
  835. margin-bottom: 24px;
  836. .dialog-group .el-dialog__body {
  837. height: 65px;
  838. }
  839. // 顶部
  840. &-top {
  841. padding: 24px 32px;
  842. background-color: #fff;
  843. border-bottom: 1px solid #ccc;
  844. border-top-left-radius: 8px;
  845. border-top-right-radius: 8px;
  846. box-shadow: 0 1px 1px #ccc;
  847. .live-title {
  848. display: flex;
  849. justify-content: space-between;
  850. &-name {
  851. padding-right: 24px;
  852. font-size: 22px;
  853. }
  854. .live-button-group {
  855. min-width: 270px;
  856. .el-button {
  857. padding: 7px 12px;
  858. border-radius: 4px;
  859. }
  860. }
  861. }
  862. }
  863. // 主容器
  864. &-container {
  865. display: flex;
  866. &-left {
  867. flex: 1;
  868. background-color: #fff;
  869. border-radius: 8px;
  870. .loading {
  871. position: relative;
  872. display: flex;
  873. align-items: center;
  874. justify-content: center;
  875. width: 100%;
  876. height: $draw-h;
  877. color: #fff;
  878. background-color: #646464;
  879. &-wrapper {
  880. text-align: center;
  881. > .el-avatar {
  882. width: 96px;
  883. height: 96px;
  884. margin-bottom: 24px;
  885. line-height: 96px;
  886. > &--icon {
  887. font-size: 36px;
  888. }
  889. }
  890. .loading-title {
  891. margin-bottom: 24px;
  892. }
  893. }
  894. }
  895. .student-parent {
  896. position: relative;
  897. width: 100%;
  898. height: $draw-h;
  899. #student {
  900. position: relative;
  901. width: 100%;
  902. height: $draw-h;
  903. background-color: $live-bc;
  904. }
  905. > .el-button {
  906. position: absolute;
  907. bottom: 40px;
  908. left: calc(50% - 44px);
  909. }
  910. .student-audio {
  911. display: flex;
  912. flex-direction: column;
  913. align-items: center;
  914. justify-content: center;
  915. width: 100%;
  916. height: 100%;
  917. background-color: #646464;
  918. .connect-name {
  919. margin: 24px 0;
  920. color: #fff;
  921. }
  922. > .el-avatar {
  923. width: 96px;
  924. height: 96px;
  925. line-height: 96px;
  926. > &--icon {
  927. font-size: 36px;
  928. }
  929. }
  930. }
  931. }
  932. // 画板
  933. #draw-parent {
  934. position: relative;
  935. width: 100%;
  936. height: $draw-h;
  937. overflow: hidden;
  938. background-color: $live-bc;
  939. // 设置屏幕画笔
  940. .draw-setting {
  941. position: absolute;
  942. bottom: 13px;
  943. left: 22px;
  944. z-index: 9999;
  945. height: 40px;
  946. padding: 6px;
  947. line-height: 28px;
  948. background-color: #a0a0a0;
  949. border-radius: 40px;
  950. > span {
  951. display: inline-block;
  952. margin-right: 10px;
  953. text-align: center;
  954. }
  955. > span.brush-shape {
  956. width: 28px;
  957. height: 28px;
  958. cursor: pointer;
  959. background-color: #fff;
  960. border-radius: 50%;
  961. }
  962. .draw-color {
  963. position: relative;
  964. top: 5px;
  965. width: 18px;
  966. height: 18px;
  967. cursor: pointer;
  968. border-radius: 50%;
  969. }
  970. .current::after {
  971. position: absolute;
  972. bottom: -7px;
  973. left: 7px;
  974. width: 4px;
  975. height: 4px;
  976. content: '';
  977. background-color: #292929;
  978. border-radius: 50%;
  979. }
  980. .draw-thickness {
  981. display: inline-flex;
  982. flex-direction: column;
  983. align-items: center;
  984. justify-content: center;
  985. width: 18px;
  986. height: 18px;
  987. vertical-align: middle;
  988. cursor: pointer;
  989. span {
  990. background-color: #000;
  991. border-radius: 50%;
  992. }
  993. }
  994. > %brush-clear,
  995. .brush-clear {
  996. width: 28px;
  997. height: 28px;
  998. margin-right: 0;
  999. cursor: pointer;
  1000. background-color: #666;
  1001. border-radius: 50%;
  1002. }
  1003. > .eraser {
  1004. @extend %brush-clear;
  1005. margin-right: 12px;
  1006. }
  1007. }
  1008. }
  1009. .button-group {
  1010. display: flex;
  1011. justify-content: space-between;
  1012. height: 48px;
  1013. padding: 0 15px;
  1014. background-color: #4d4d4d;
  1015. border-bottom-left-radius: 5px;
  1016. .svg-icon {
  1017. font-size: 20px;
  1018. }
  1019. &-left {
  1020. .stop-group {
  1021. color: #fff;
  1022. }
  1023. > .icon-button {
  1024. display: inline-block;
  1025. height: 100%;
  1026. padding: 14px 16px;
  1027. cursor: pointer;
  1028. &:active,
  1029. &:hover {
  1030. background-color: #3d3d3d;
  1031. }
  1032. }
  1033. }
  1034. }
  1035. }
  1036. &-right {
  1037. width: 485px;
  1038. height: 568px;
  1039. background-color: #fff;
  1040. .live-teacher-lens {
  1041. position: relative;
  1042. display: flex;
  1043. overflow: hidden;
  1044. #live {
  1045. width: 272px;
  1046. height: 152px;
  1047. background-color: $live-bc;
  1048. }
  1049. .live-wrapper {
  1050. position: absolute;
  1051. display: flex;
  1052. width: 272px;
  1053. height: 40px;
  1054. padding: 0 16px;
  1055. line-height: 40px;
  1056. color: #fff;
  1057. background-color: rgba(0, 0, 0, 70%);
  1058. transition: all 300ms ease-in 0s;
  1059. > div:first-child {
  1060. flex: 6;
  1061. }
  1062. &-right {
  1063. flex: 4;
  1064. text-align: right;
  1065. .svg-icon {
  1066. margin-right: 18px;
  1067. cursor: pointer;
  1068. }
  1069. }
  1070. }
  1071. .live-operate {
  1072. display: flex;
  1073. flex: 1;
  1074. flex-direction: column;
  1075. align-items: center;
  1076. justify-content: center;
  1077. &-icon {
  1078. display: flex;
  1079. padding-top: 18px;
  1080. > .svg-icon {
  1081. margin-left: 24px;
  1082. cursor: pointer;
  1083. }
  1084. }
  1085. }
  1086. }
  1087. // 聊天窗口
  1088. &-chat {
  1089. display: flex;
  1090. flex-direction: column;
  1091. justify-content: space-between;
  1092. height: 417px;
  1093. border: 1px solid #ccc;
  1094. .chat-top {
  1095. display: flex;
  1096. justify-content: space-between;
  1097. padding: 15px 15px 10px;
  1098. color: #959595;
  1099. border-bottom: 1px solid #e6e6e6;
  1100. .chat-bans {
  1101. cursor: pointer;
  1102. }
  1103. .allow-chat {
  1104. margin-right: 12px;
  1105. }
  1106. }
  1107. .chat-window {
  1108. position: relative;
  1109. width: 100%;
  1110. height: 100%;
  1111. overflow: hidden;
  1112. &-ul {
  1113. position: absolute;
  1114. top: 0;
  1115. left: 0;
  1116. width: 100%;
  1117. height: 100%;
  1118. overflow: auto;
  1119. .msg-normal {
  1120. padding: 7px 16px;
  1121. }
  1122. }
  1123. }
  1124. .chat-speak {
  1125. padding: 16px;
  1126. }
  1127. }
  1128. }
  1129. }
  1130. .material-list {
  1131. padding: 12px;
  1132. background-color: #fff;
  1133. border-bottom: 1px solid #ccc;
  1134. .el-tag {
  1135. padding: 0 20px;
  1136. margin: 0 0 8px 12px;
  1137. color: $color;
  1138. cursor: pointer;
  1139. border-color: #e5e5e5;
  1140. .svg-icon {
  1141. margin-right: 8px;
  1142. font-size: 18px;
  1143. }
  1144. }
  1145. }
  1146. // 学员列表
  1147. .student-list {
  1148. width: 100%;
  1149. font-size: 14px;
  1150. &-title {
  1151. padding: 24px 16px;
  1152. background-color: #fff;
  1153. }
  1154. &-container {
  1155. padding: 16px 20px;
  1156. background-color: #f2f2f2;
  1157. ul {
  1158. display: flex;
  1159. flex-wrap: wrap;
  1160. li {
  1161. display: flex;
  1162. flex-direction: column;
  1163. align-items: center;
  1164. width: 120px;
  1165. height: 144px;
  1166. margin: 0 8px 8px 0;
  1167. background-color: #fff;
  1168. .li-top {
  1169. position: relative;
  1170. flex: 1;
  1171. width: 100%;
  1172. min-height: 72px;
  1173. text-align: center;
  1174. background-color: #000;
  1175. .svg-icon {
  1176. position: absolute;
  1177. top: 10px;
  1178. right: 10px;
  1179. }
  1180. .el-avatar--icon {
  1181. margin-top: 16px;
  1182. }
  1183. }
  1184. .li-bottom {
  1185. flex: 1;
  1186. min-height: 72px;
  1187. padding: 16px 8px;
  1188. &-operate {
  1189. padding: 0 24px;
  1190. .svg-icon {
  1191. margin: 8px 4px 0;
  1192. font-size: 18px;
  1193. cursor: pointer;
  1194. }
  1195. }
  1196. }
  1197. }
  1198. }
  1199. }
  1200. }
  1201. }
  1202. </style>