liveClassDetail.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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
  98. align="center"
  99. prop="musicGroupName"
  100. label="乐团"
  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'"
  164. style="width: 320px"
  165. :src="activeVideo.url"
  166. ref="dialogVideo"
  167. controls="controls"
  168. :core="HLSCore"
  169. >
  170. 您的浏览器不支持视频播放
  171. </vue-core-video-player>
  172. <vue-core-video-player
  173. v-else
  174. style="width: 640px"
  175. :src="activeVideo.url"
  176. ref="dialogVideo"
  177. controls="controls"
  178. :core="HLSCore"
  179. >
  180. 您的浏览器不支持视频播放
  181. </vue-core-video-player>
  182. </div>
  183. </el-dialog>
  184. </div>
  185. </template>
  186. <script>
  187. import pagination from "@/components/Pagination/index";
  188. import cleanDeep from "clean-deep";
  189. import qs from "qs";
  190. import { getLiveBroadcastRoomDetail, getLiveBroadcastRoomDetailList } from "./api";
  191. import { Export } from "@/utils/downLoadFile";
  192. import videoPlace from "./images/video-place.png";
  193. import HLSCore from "@core-player/playcore-hls";
  194. export default {
  195. components: { pagination },
  196. data() {
  197. return {
  198. searchForm: {
  199. search: "",
  200. },
  201. tableList: [],
  202. detail: {
  203. list: null,
  204. liveRemark: "",
  205. roomTitle: "",
  206. roomUid: "",
  207. speakerName: "",
  208. totalLikeNum: 0,
  209. totalLiveTime: 0,
  210. totalLookNum: 0,
  211. },
  212. rules: {
  213. // 分页规则
  214. limit: 10, // 限制显示条数
  215. page: 1, // 当前页
  216. total: 0, // 总条数
  217. page_size: [10, 20, 40, 50], // 选择限制显示条数
  218. },
  219. videoList: [],
  220. payVisible: false,
  221. videoVisible: false,
  222. activeVideo: { endTime: "" },
  223. HLSCore,
  224. videoPlace,
  225. };
  226. },
  227. mounted() {
  228. this.getDetail();
  229. this.getList();
  230. },
  231. methods: {
  232. onCancel() {
  233. this.$router.push("/liveClassManager");
  234. this.$store.dispatch("delVisitedViews", this.$route);
  235. },
  236. search() {
  237. this.rules.page = 1;
  238. this.getList();
  239. },
  240. onReSet() {
  241. this.searchForm.search = "";
  242. this.search();
  243. },
  244. async getList() {
  245. try {
  246. let obj = {
  247. rows: this.rules.limit,
  248. page: this.rules.page,
  249. roomUid: this.$route.query.roomUid,
  250. search: this.searchForm.search,
  251. };
  252. const res = await getLiveBroadcastRoomDetailList(obj);
  253. this.tableList = res.data.rows;
  254. this.rules.total = res.data.total;
  255. } catch (e) {
  256. console.log(e);
  257. }
  258. },
  259. async getDetail() {
  260. try {
  261. let obj = {
  262. roomUid: this.$route.query.roomUid,
  263. };
  264. const res = await getLiveBroadcastRoomDetail(obj);
  265. this.detail = { ...res.data };
  266. this.videoList = res.data.videoList;
  267. } catch (e) {
  268. console.log(e);
  269. }
  270. },
  271. opneVideo(row) {
  272. this.activeVideo = row;
  273. this.videoVisible = true;
  274. },
  275. exportStudent() {
  276. let obj = {
  277. rows: this.rules.limit,
  278. page: this.rules.page,
  279. roomUid: this.$route.query.roomUid,
  280. search: this.searchForm.search,
  281. };
  282. Export(
  283. this,
  284. {
  285. url: "/api-web/export/liveBroadcastRoomMember",
  286. fileName: `"${this.detail.roomTitle}"参与学员.xls`,
  287. method: "post",
  288. params: qs.stringify(cleanDeep(obj)),
  289. },
  290. `您确定导出"${this.detail.roomTitle}"参与学员?`
  291. );
  292. },
  293. },
  294. };
  295. </script>
  296. <style lang="scss" scoped>
  297. .infoWrap {
  298. // width: 500px;
  299. }
  300. .sub.teacherName {
  301. margin-right: 10px;
  302. }
  303. .fontBtn {
  304. font-size: 18px;
  305. }
  306. .teacherName,
  307. .liveRemark {
  308. font-size: 16px;
  309. color: #222325;
  310. line-height: 30px;
  311. font-weight: 600;
  312. margin-right: 60px;
  313. span {
  314. color: #666666;
  315. font-weight: 400;
  316. }
  317. }
  318. .titleWrap {
  319. position: relative;
  320. // display: flex;
  321. // flex-direction: row;
  322. // align-items: center;
  323. padding: 5px;
  324. h2 {
  325. margin-right: 10px;
  326. }
  327. .squrtTitle {
  328. position: relative;
  329. font-size: 18px;
  330. font-family: PingFangSC-Medium, PingFang SC;
  331. font-weight: 600;
  332. color: #222325;
  333. line-height: 25px;
  334. margin-bottom: 25px;
  335. .squrt {
  336. position: absolute;
  337. left: -12px;
  338. top: 5px;
  339. width: 3px;
  340. height: 16px;
  341. background-color: var(--color-primary);
  342. }
  343. }
  344. .row {
  345. display: flex;
  346. flex-direction: row;
  347. align-items: center;
  348. }
  349. }
  350. .statistic .statistic-content > span {
  351. font-size: 16px;
  352. }
  353. .lineWrap {
  354. display: flex;
  355. flex-direction: row;
  356. align-items: center;
  357. flex-wrap: wrap;
  358. .linkItem {
  359. display: flex;
  360. flex-direction: column;
  361. align-items: center;
  362. margin-right: 5px;
  363. cursor: pointer;
  364. position: relative;
  365. &:hover {
  366. color: var(--color-primary);
  367. .lineTitle {
  368. color: var(--color-primary);
  369. font-weight: bold;
  370. }
  371. }
  372. .linkItemWrap {
  373. position: relative;
  374. .linkIcon {
  375. font-size: 25px;
  376. position: absolute;
  377. top: 50%;
  378. margin-top: -13px;
  379. left: 50%;
  380. margin-left: -13px;
  381. color: #fff;
  382. }
  383. }
  384. }
  385. .lineTitle {
  386. height: 40px;
  387. line-height: 40px;
  388. color: rgba(102, 102, 102, 0.9);
  389. padding: 0 17px;
  390. overflow: hidden;
  391. margin-bottom: 10px;
  392. position: relative;
  393. border-radius: 4px;
  394. }
  395. }
  396. .activeVideoWrap {
  397. display: flex;
  398. flex-direction: column;
  399. align-items: center;
  400. }
  401. </style>