liveClassDetail.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <template>
  2. <div class="m-container">
  3. <h2>
  4. <el-page-header @back="onCancel" content="直播详情"></el-page-header>
  5. </h2>
  6. <div class="m-core">
  7. <div class="titleWrap">
  8. <h2 class="squrtTitle">
  9. <div class="squrt"></div>
  10. 直播数据
  11. </h2>
  12. <el-row class="row">
  13. <p class="teacherName">
  14. 主讲人:<span>{{ detail.speakerName }}</span>
  15. </p>
  16. <p class="teacherName sub">
  17. 主题:<span>{{ detail.roomTitle }}</span>
  18. </p>
  19. <el-button
  20. type="text"
  21. class="fontBtn"
  22. @click="payVisible = true"
  23. v-if="videoList.length > 0"
  24. >直播回放 <i class="el-icon-video-play"></i
  25. ></el-button>
  26. </el-row>
  27. <p class="liveRemark">
  28. 直播内容:<span>{{ detail.liveRemark }}</span>
  29. </p>
  30. </div>
  31. <div class="infoWrap">
  32. <statistic class="statistic" :cols="0">
  33. <statistic-item>
  34. <span> 参与学员 </span>
  35. <span>{{ detail.totalLookNum }}</span>
  36. </statistic-item>
  37. <statistic-item>
  38. <span> 累计点赞 </span>
  39. <span>{{ detail.totalLikeNum }}</span>
  40. </statistic-item>
  41. <statistic-item>
  42. <span> 直播时长(分钟) </span>
  43. <span>{{ detail.totalLiveTime }}</span>
  44. </statistic-item>
  45. </statistic>
  46. </div>
  47. <save-form
  48. :inline="true"
  49. :model="searchForm"
  50. @submit="search"
  51. @reset="onReSet"
  52. ref="searchForm"
  53. >
  54. <el-form-item>
  55. <el-input
  56. v-model.trim="searchForm.search"
  57. clearable
  58. @keyup.enter.native="
  59. (e) => {
  60. e.target.blur();
  61. $refs.searchForm.save();
  62. search();
  63. }
  64. "
  65. placeholder="学员编号/姓名"
  66. ></el-input>
  67. </el-form-item>
  68. <el-form-item>
  69. <el-button native-type="submit" type="primary">搜索</el-button>
  70. <el-button native-type="reset" type="danger">重置</el-button>
  71. <el-button
  72. type="primary"
  73. v-if="tableList.length > 0"
  74. v-permission="'export/liveBroadcastRoomMember'"
  75. @click="exportStudent"
  76. >导出</el-button
  77. >
  78. </el-form-item>
  79. </save-form>
  80. <div class="tableWrap">
  81. <el-table
  82. style="width: 100%"
  83. :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
  84. :data="tableList"
  85. >
  86. <el-table-column
  87. align="center"
  88. prop="studentId"
  89. label="学员编号"
  90. ></el-table-column>
  91. <el-table-column
  92. align="center"
  93. prop="studentName"
  94. label="学员姓名"
  95. ></el-table-column>
  96. <el-table-column align="center" prop="organName" label="分部"></el-table-column>
  97. <el-table-column align="center" prop="musicGroupName" label="乐团">
  98. <template slot-scope="scope">
  99. <tooltip :content="scope.row.musicGroupName" />
  100. </template>
  101. </el-table-column>
  102. <el-table-column align="center" prop="phone" label="手机号"></el-table-column>
  103. <el-table-column align="center" prop="subName" label="声部"></el-table-column>
  104. <el-table-column
  105. align="center"
  106. prop="joinTime"
  107. label="进入时间"
  108. ></el-table-column>
  109. <el-table-column align="center" prop="totalViewTime" label="观看时长">
  110. <template slot-scope="scope">
  111. <div>{{ scope.row.totalViewTime }}分钟</div>
  112. </template>
  113. </el-table-column>
  114. </el-table>
  115. <pagination
  116. sync
  117. :total.sync="rules.total"
  118. :page.sync="rules.page"
  119. :limit.sync="rules.limit"
  120. :page-sizes="rules.page_size"
  121. @pagination="getList"
  122. />
  123. </div>
  124. </div>
  125. <el-dialog
  126. :visible.sync="payVisible"
  127. v-if="payVisible"
  128. width="500px"
  129. title="直播回放"
  130. >
  131. <div class="lineWrap">
  132. <div
  133. class="linkItem"
  134. v-for="(item, index) in videoList"
  135. :key="index"
  136. @click="opneVideo(item)"
  137. >
  138. <div class="linkItemWrap">
  139. <!-- <video width="110px" :src="item.url"></video> -->
  140. <img width="110px" :src="videoPlace" alt="" />
  141. <i class="el-icon-video-play linkIcon"></i>
  142. </div>
  143. <p :underline="false" type="text" class="lineTitle">
  144. {{ item.endTime | dateForMinFormat }}
  145. </p>
  146. </div>
  147. </div>
  148. <div slot="footer" class="dialog-footer">
  149. <!-- <el-button @click="payVisible = false">取 消</el-button> -->
  150. <el-button type="primary" @click="payVisible = false">确 定</el-button>
  151. </div></el-dialog
  152. >
  153. <el-dialog
  154. :title="activeVideo.endTime | dateForMinFormat"
  155. width="680px"
  156. append-to-body
  157. :visible.sync="videoVisible"
  158. v-if="videoVisible"
  159. >
  160. <!-- activeUrl -->
  161. <div class="activeVideoWrap">
  162. <vue-core-video-player
  163. v-if="activeVideo.os == 'mobile' && activeVideo.videoType != 'm3u8'"
  164. style="width: 320px"
  165. :src="activeVideo.url"
  166. ref="dialogVideo"
  167. >
  168. 您的浏览器不支持视频播放
  169. </vue-core-video-player>
  170. <vue-core-video-player
  171. v-if="activeVideo.os == 'mobile' && activeVideo.videoType == 'm3u8'"
  172. style="width: 320px"
  173. :src="activeVideo.url"
  174. ref="dialogVideo"
  175. :core="HLSCore"
  176. >
  177. 您的浏览器不支持视频播放
  178. </vue-core-video-player>
  179. <vue-core-video-player
  180. v-if="activeVideo.os == 'client'"
  181. style="width: 640px"
  182. :src="activeVideo.url"
  183. ref="dialogVideo"
  184. :core="HLSCore"
  185. >
  186. 您的浏览器不支持视频播放
  187. </vue-core-video-player>
  188. <vue-core-video-player
  189. v-if="activeVideo.os == 'pc'"
  190. style="width: 640px"
  191. :src="activeVideo.url"
  192. ref="dialogVideo"
  193. >
  194. 您的浏览器不支持视频播放
  195. </vue-core-video-player>
  196. </div>
  197. </el-dialog>
  198. </div>
  199. </template>
  200. <script>
  201. import pagination from "@/components/Pagination/index";
  202. import cleanDeep from "clean-deep";
  203. import qs from "qs";
  204. import { getLiveBroadcastRoomDetail, getLiveBroadcastRoomDetailList } from "./api";
  205. import { Export } from "@/utils/downLoadFile";
  206. import videoPlace from "./images/video-place.png";
  207. import HLSCore from "@core-player/playcore-hls";
  208. export default {
  209. components: { pagination },
  210. data() {
  211. return {
  212. searchForm: {
  213. search: "",
  214. },
  215. tableList: [],
  216. detail: {
  217. list: null,
  218. liveRemark: "",
  219. roomTitle: "",
  220. roomUid: "",
  221. speakerName: "",
  222. totalLikeNum: 0,
  223. totalLiveTime: 0,
  224. totalLookNum: 0,
  225. },
  226. rules: {
  227. // 分页规则
  228. limit: 10, // 限制显示条数
  229. page: 1, // 当前页
  230. total: 0, // 总条数
  231. page_size: [10, 20, 40, 50], // 选择限制显示条数
  232. },
  233. videoList: [],
  234. payVisible: false,
  235. videoVisible: false,
  236. activeVideo: { endTime: "" },
  237. HLSCore,
  238. videoPlace,
  239. };
  240. },
  241. mounted() {
  242. this.getDetail();
  243. this.getList();
  244. },
  245. methods: {
  246. onCancel() {
  247. this.$router.push("/liveClassManager");
  248. this.$store.dispatch("delVisitedViews", this.$route);
  249. },
  250. search() {
  251. this.rules.page = 1;
  252. this.getList();
  253. },
  254. onReSet() {
  255. this.searchForm.search = "";
  256. this.search();
  257. },
  258. async getList() {
  259. try {
  260. let obj = {
  261. rows: this.rules.limit,
  262. page: this.rules.page,
  263. roomUid: this.$route.query.roomUid,
  264. search: this.searchForm.search,
  265. };
  266. const res = await getLiveBroadcastRoomDetailList(obj);
  267. this.tableList = res.data.rows;
  268. this.rules.total = res.data.total;
  269. } catch (e) {
  270. console.log(e);
  271. }
  272. },
  273. async getDetail() {
  274. try {
  275. let obj = {
  276. roomUid: this.$route.query.roomUid,
  277. };
  278. const res = await getLiveBroadcastRoomDetail(obj);
  279. this.detail = { ...res.data };
  280. this.videoList = res.data.videoList;
  281. } catch (e) {
  282. console.log(e);
  283. }
  284. },
  285. opneVideo(row) {
  286. console.log(row, "opneVideo");
  287. this.activeVideo = row;
  288. this.videoVisible = true;
  289. },
  290. exportStudent() {
  291. let obj = {
  292. rows: this.rules.limit,
  293. page: this.rules.page,
  294. roomUid: this.$route.query.roomUid,
  295. search: this.searchForm.search,
  296. };
  297. Export(
  298. this,
  299. {
  300. url: "/api-web/export/liveBroadcastRoomMember",
  301. fileName: `"${this.detail.roomTitle}"参与学员.xls`,
  302. method: "post",
  303. params: qs.stringify(cleanDeep(obj)),
  304. },
  305. `您确定导出"${this.detail.roomTitle}"参与学员?`
  306. );
  307. },
  308. },
  309. };
  310. </script>
  311. <style lang="scss" scoped>
  312. .infoWrap {
  313. // width: 500px;
  314. }
  315. .sub.teacherName {
  316. margin-right: 10px;
  317. }
  318. .fontBtn {
  319. font-size: 18px;
  320. }
  321. .teacherName,
  322. .liveRemark {
  323. font-size: 16px;
  324. color: #222325;
  325. line-height: 30px;
  326. font-weight: 600;
  327. margin-right: 60px;
  328. span {
  329. color: #666666;
  330. font-weight: 400;
  331. }
  332. }
  333. .titleWrap {
  334. position: relative;
  335. // display: flex;
  336. // flex-direction: row;
  337. // align-items: center;
  338. padding: 5px;
  339. h2 {
  340. margin-right: 10px;
  341. }
  342. .squrtTitle {
  343. position: relative;
  344. font-size: 18px;
  345. font-family: PingFangSC-Medium, PingFang SC;
  346. font-weight: 600;
  347. color: #222325;
  348. line-height: 25px;
  349. margin-bottom: 25px;
  350. .squrt {
  351. position: absolute;
  352. left: -12px;
  353. top: 5px;
  354. width: 3px;
  355. height: 16px;
  356. background-color: var(--color-primary);
  357. }
  358. }
  359. .row {
  360. display: flex;
  361. flex-direction: row;
  362. align-items: center;
  363. }
  364. }
  365. .statistic .statistic-content > span {
  366. font-size: 16px;
  367. }
  368. .lineWrap {
  369. display: flex;
  370. flex-direction: row;
  371. align-items: center;
  372. flex-wrap: wrap;
  373. .linkItem {
  374. display: flex;
  375. flex-direction: column;
  376. align-items: center;
  377. margin-right: 5px;
  378. cursor: pointer;
  379. position: relative;
  380. &:hover {
  381. color: var(--color-primary);
  382. .lineTitle {
  383. color: var(--color-primary);
  384. font-weight: bold;
  385. }
  386. }
  387. .linkItemWrap {
  388. position: relative;
  389. .linkIcon {
  390. font-size: 25px;
  391. position: absolute;
  392. top: 50%;
  393. margin-top: -13px;
  394. left: 50%;
  395. margin-left: -13px;
  396. color: #fff;
  397. }
  398. }
  399. }
  400. .lineTitle {
  401. height: 40px;
  402. line-height: 40px;
  403. color: rgba(102, 102, 102, 0.9);
  404. padding: 0 17px;
  405. overflow: hidden;
  406. margin-bottom: 10px;
  407. position: relative;
  408. border-radius: 4px;
  409. }
  410. }
  411. .activeVideoWrap {
  412. display: flex;
  413. flex-direction: column;
  414. align-items: center;
  415. }
  416. </style>