index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. <template>
  2. <section class="videos-container">
  3. <div class="videos-wrap">
  4. <div class="bread-crumbs">
  5. <a
  6. :href="`${wwwURL}/${$store.state.locale}`"
  7. class="ele link"
  8. target="_blank">{{ $t("home") }}</a>
  9. <span class="ele arrow">></span>
  10. <a
  11. :href="`/${$store.state.locale}`"
  12. class="ele link">{{ $t("community") }}</a>
  13. <span class="ele arrow">></span>
  14. <a
  15. class="ele link disable"
  16. href="javascript:void(0);">{{ $t("tabVideos") }}</a>
  17. </div>
  18. <ul
  19. v-if="videoArr.length>0"
  20. class="video-grid">
  21. <li
  22. v-for="(item,index) in videoArr"
  23. :key="index"
  24. class="video-box">
  25. <video-item
  26. :video-obj="item"
  27. @handlePlayVideo="handlePlayVideo"/>
  28. </li>
  29. </ul>
  30. <template v-else>
  31. <empty :empty-obj="{'tips': $t('noVideoData')}"/>
  32. </template>
  33. <div
  34. v-if="isMounted&&totalCount>pageSize"
  35. class="pagination-container">
  36. <paginate
  37. v-model="page"
  38. :page-count="Math.ceil(totalCount/pageSize)"
  39. :page-range="3"
  40. :margin-pages="2"
  41. :prev-text="$t('paginationPrev')"
  42. :next-text="$t('paginationNext')"
  43. :click-handler="paginationClickCallback"
  44. :container-class="'pagination'"
  45. :page-class="'page-item'"
  46. :page-link-class="'page-item-link'"
  47. :prev-class="'page-prev-item'"
  48. :prev-link-class="'page-prev-item-link'"
  49. :next-class="'page-next-item'"
  50. :next-link-class="'page-next-item-link'"
  51. :hide-prev-next="true"/>
  52. </div>
  53. </div>
  54. <div
  55. v-if="currentVideoItem&&currentVideoItem.videoUrl"
  56. class="video-mask"
  57. @click="handleCloseVideo"
  58. >
  59. <div
  60. class="video-inner animated zoomIn faster"
  61. @click.stop="handleStopBubble">
  62. <video
  63. id="myVideo"
  64. :poster="currentVideoItem.coverImg"
  65. class="video-js vjs-big-play-centered video-player"
  66. controls
  67. preload="auto"
  68. autoplay
  69. />
  70. <span
  71. class="iconfont icon-guanbi1"
  72. @click="handleCloseVideo"/>
  73. </div>
  74. </div>
  75. </section>
  76. </template>
  77. <script>
  78. import Vue from 'vue'
  79. import { php_api_video_list, php_api_video_hit } from '~/common/apis.js'
  80. import { loadSrc } from '~/common/utils.js'
  81. import VideoItem from '~/components/videos/VideoItem'
  82. import Empty from '~/components/common/Empty.vue'
  83. import { wwwURL } from '~/common/config.js'
  84. const PAGE_SIZE = 20
  85. export default {
  86. async asyncData({ app, query, store }) {
  87. let { page } = query
  88. if (page) {
  89. page = parseInt(page)
  90. } else {
  91. page = 1
  92. }
  93. let pageSize = PAGE_SIZE
  94. let totalCount = 0
  95. let videoArr = await app.$axios
  96. .$post(php_api_video_list, { page, pageSize })
  97. .then(res => {
  98. if (res) {
  99. let { code, data } = res
  100. code = parseInt(code)
  101. if (code === 0 && data) {
  102. totalCount = data.totalCount
  103. if (Array.isArray(data.list)) {
  104. return data.list
  105. }
  106. }
  107. }
  108. return []
  109. })
  110. .catch(e => {
  111. return []
  112. })
  113. return { page, pageSize, videoArr, totalCount }
  114. },
  115. validate({ query, store, route, redirect }) {
  116. if (!query.hasOwnProperty('page')) {
  117. return true
  118. }
  119. let { page } = query
  120. if (/^[0-9]+$/.test(page) && parseInt(page) !== 0 && parseInt(page) !== 1) {
  121. return true
  122. } else {
  123. let { fullPath } = route
  124. let { locales, locale } = store.state
  125. let flag = false
  126. locales.some(val => {
  127. if (fullPath.indexOf(`/${val}`) === 0) {
  128. flag = true
  129. return true
  130. }
  131. })
  132. if (flag) {
  133. return redirect(`/${locale}/videos`)
  134. } else {
  135. return redirect(`/videos`)
  136. }
  137. }
  138. },
  139. head() {
  140. return {
  141. script: [
  142. {
  143. type: 'text/javascript',
  144. src: '//static.hoolihome.com/common/vuejs-paginate/index.js'
  145. }
  146. ]
  147. }
  148. },
  149. components: { VideoItem, Empty },
  150. data() {
  151. return {
  152. wwwURL,
  153. page: 1,
  154. pageSize: PAGE_SIZE,
  155. videoArr: [],
  156. currentVideoItem: null,
  157. myVideoPlayer: null,
  158. totalCount: 0,
  159. isMounted: false
  160. }
  161. },
  162. scrollToTop: true,
  163. beforeCreate() {
  164. if (process.client && window.VuejsPaginate) {
  165. Vue.component('paginate', window.VuejsPaginate)
  166. }
  167. },
  168. mounted() {
  169. this.isMounted = true
  170. this.loadVideoSrc()
  171. this.$hooliAnalysis.upload({
  172. et: this.$C.EVENT_TYPE.show
  173. })
  174. },
  175. methods: {
  176. handlePlayVideo(videoItem) {
  177. if (!window.videojs) {
  178. return false
  179. }
  180. this.currentVideoItem = videoItem
  181. this.$nextTick(() => {
  182. if (!this.myVideoPlayer) {
  183. this.myVideoPlayer = window.videojs('myVideo', {
  184. autoplay: true,
  185. controls: true
  186. })
  187. }
  188. let mimeType = ''
  189. if (videoItem.videoUrl.toLowerCase().indexOf('.m3u8') !== -1) {
  190. mimeType = 'application/x-mpegURL'
  191. } else if (videoItem.videoUrl.toLowerCase().indexOf('.mp4') !== -1) {
  192. mimeType = 'video/mp4'
  193. }
  194. // videoItem.videoUrl =
  195. // 'https://files.hoolihome.com/m3u8/index/1116/20181116170224_544.m3u8'
  196. this.myVideoPlayer.src({
  197. src: videoItem.videoUrl,
  198. type: mimeType
  199. })
  200. this.myVideoPlayer.play()
  201. })
  202. this.updateVideoBrowseVolume(videoItem.id)
  203. },
  204. handleCloseVideo() {
  205. if (this.myVideoPlayer) {
  206. this.myVideoPlayer.pause()
  207. this.myVideoPlayer.dispose()
  208. this.myVideoPlayer = null
  209. }
  210. this.currentVideoItem = null
  211. },
  212. handleStopBubble() {},
  213. loadVideoSrc() {
  214. loadSrc(
  215. '//static.hoolihome.com/common/video.js/video-js.min.css',
  216. 'VIDEO_LINK_ID',
  217. 'css'
  218. ).then(() => {
  219. loadSrc(
  220. '//static.hoolihome.com/common/video.js/video.min.js',
  221. 'VIDEO_SCRIPT_ID',
  222. 'js'
  223. )
  224. })
  225. },
  226. loadData() {
  227. this.$axios
  228. .$post(php_api_video_list, {
  229. page: this.page,
  230. pageSize: this.pageSize
  231. })
  232. .then(res => {
  233. if (res) {
  234. let { code, data } = res
  235. code = parseInt(code)
  236. if (code === 0 && data && Array.isArray(data.list) && data) {
  237. let { list } = data
  238. if (Array.isArray(list) && list.length > 0) {
  239. this.videoArr = data.list
  240. return false
  241. }
  242. }
  243. }
  244. this.videoArr = []
  245. })
  246. .catch(e => {
  247. this.videoArr = []
  248. })
  249. },
  250. paginationClickCallback(pageNum) {
  251. let { path, query } = this.$route
  252. let queryArr = []
  253. query.page = pageNum
  254. for (let key in query) {
  255. queryArr.push(`${key}=${query[key]}`)
  256. }
  257. window.location.href = `${path}?${queryArr.join('&')}`
  258. },
  259. updateVideoBrowseVolume(id) {
  260. this.$axios
  261. .$post(php_api_video_hit, { id })
  262. .then(res => {})
  263. .catch(e => {})
  264. }
  265. }
  266. }
  267. </script>
  268. <style lang="less">
  269. .video-inner {
  270. .vjs-paused .vjs-big-play-button,
  271. .vjs-paused.vjs-has-started .vjs-big-play-button {
  272. display: block;
  273. }
  274. .video-js .vjs-big-play-button {
  275. font-size: 2.5em;
  276. line-height: 2.3em;
  277. height: 2.5em;
  278. width: 2.5em;
  279. -webkit-border-radius: 2.5em;
  280. -moz-border-radius: 2.5em;
  281. border-radius: 2.5em;
  282. background-color: #73859f;
  283. background-color: rgba(115, 133, 159, 0.5);
  284. border-width: 0.15em;
  285. margin-top: -1.25em;
  286. // margin-left: -1.75em;
  287. }
  288. /* 中间的播放箭头 */
  289. .vjs-big-play-button .vjs-icon-placeholder {
  290. font-size: 1.63em;
  291. }
  292. /* 加载圆圈 */
  293. .vjs-loading-spinner {
  294. font-size: 2.5em;
  295. width: 2em;
  296. height: 2em;
  297. border-radius: 1em;
  298. margin-top: -1em;
  299. margin-left: -1.5em;
  300. }
  301. .video-js.vjs-playing .vjs-tech {
  302. pointer-events: auto;
  303. }
  304. .video-js .vjs-big-play-button .vjs-icon-placeholder:before,
  305. .video-js .vjs-modal-dialog,
  306. .vjs-button > .vjs-icon-placeholder:before,
  307. .vjs-modal-dialog .vjs-modal-dialog-content {
  308. position: relative;
  309. }
  310. }
  311. .videos-container {
  312. .pagination {
  313. display: flex;
  314. justify-content: flex-start;
  315. align-items: center;
  316. .page-prev-item,
  317. .page-next-item {
  318. height: 24px;
  319. line-height: 24px;
  320. margin-right: 2px;
  321. padding: 0 7px;
  322. font-size: 12px;
  323. font-family: PingFangSC-Regular;
  324. font-weight: 400;
  325. color: rgba(255, 255, 255, 1);
  326. background-color: #ed5f48;
  327. border-radius: 2px;
  328. outline: none;
  329. &.disabled {
  330. pointer-events: none;
  331. background-color: #d6d6d6;
  332. }
  333. .page-prev-item-link,
  334. .page-next-item-link {
  335. display: inline-block;
  336. outline: none;
  337. }
  338. }
  339. .page-item {
  340. height: 24px;
  341. line-height: 24px;
  342. margin-right: 2px;
  343. outline: none;
  344. border-radius: 2px;
  345. &.active {
  346. pointer-events: none;
  347. background-color: #ed5f48;
  348. .page-item-link {
  349. color: #ffffff;
  350. }
  351. }
  352. &:not(.active) {
  353. &:hover {
  354. background-color: #eeeeee;
  355. }
  356. }
  357. .page-item-link {
  358. display: inline-block;
  359. padding: 3px 7px;
  360. font-size: 12px;
  361. font-family: PingFangSC-Regular;
  362. font-weight: 400;
  363. color: rgba(102, 102, 102, 1);
  364. outline: none;
  365. }
  366. }
  367. }
  368. }
  369. </style>
  370. <style lang="less" scoped>
  371. .videos-container {
  372. .videos-wrap {
  373. margin: 0 auto;
  374. .bread-crumbs {
  375. display: flex;
  376. align-items: center;
  377. width: 1200px;
  378. height: 50px;
  379. margin: 0 auto;
  380. padding: 0 5px;
  381. .ele {
  382. display: inline-block;
  383. height: 17px;
  384. line-height: 17px;
  385. margin-right: 10px;
  386. font-size: 12px;
  387. font-family: PingFangSC-Regular;
  388. font-weight: 400;
  389. color: rgba(153, 153, 153, 1);
  390. &.link {
  391. &:hover {
  392. color: #ed5f48;
  393. }
  394. &.disable {
  395. pointer-events: none;
  396. }
  397. }
  398. }
  399. }
  400. .video-grid {
  401. display: flex;
  402. flex-flow: row wrap;
  403. width: 1200px;
  404. margin: 0 auto;
  405. padding-left: 5px;
  406. .video-box {
  407. width: 272px;
  408. height: 265px;
  409. margin: 0 34px 30px 0;
  410. &:nth-child(4n + 4) {
  411. margin-right: 0px;
  412. }
  413. }
  414. }
  415. .pagination-container {
  416. width: 1200px;
  417. margin: 10px auto 90px;
  418. padding: 0 5px;
  419. }
  420. }
  421. .video-mask {
  422. position: fixed;
  423. top: 0;
  424. left: 0;
  425. bottom: 0;
  426. right: 0;
  427. z-index: 10;
  428. display: flex;
  429. justify-content: center;
  430. align-items: center;
  431. background-color: rgba(0, 0, 0, 0.7);
  432. cursor: pointer;
  433. -webkit-transition: opacity 350ms cubic-bezier(0.165, 0.84, 0.44, 1);
  434. transition: opacity 350ms cubic-bezier(0.165, 0.84, 0.44, 1);
  435. .video-inner {
  436. position: relative;
  437. width: 711px;
  438. height: 401px;
  439. box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.17);
  440. border-radius: 8px;
  441. background-color: #000000;
  442. animation-duration: 0.3s;
  443. .video-player {
  444. position: relative;
  445. width: 100%;
  446. height: 100%;
  447. border-radius: 8px;
  448. }
  449. .icon-guanbi1 {
  450. position: absolute;
  451. top: -27px;
  452. right: 0px;
  453. color: #ffffff;
  454. font-size: 12px;
  455. cursor: pointer;
  456. }
  457. }
  458. }
  459. }
  460. @-webkit-keyframes zoomIn {
  461. 0% {
  462. -webkit-transform: scale3d(0.3, 0.3, 0.3);
  463. opacity: 0;
  464. transform: scale3d(0.3, 0.3, 0.3);
  465. }
  466. 50% {
  467. opacity: 1;
  468. }
  469. }
  470. @keyframes zoomIn {
  471. 0% {
  472. -webkit-transform: scale3d(0.3, 0.3, 0.3);
  473. opacity: 0;
  474. transform: scale3d(0.3, 0.3, 0.3);
  475. }
  476. 50% {
  477. opacity: 1;
  478. }
  479. }
  480. .zoomIn {
  481. -webkit-animation-name: zoomIn;
  482. animation-name: zoomIn;
  483. }
  484. .animated {
  485. -webkit-animation-duration: 1s;
  486. -webkit-animation-fill-mode: both;
  487. animation-duration: 1s;
  488. animation-fill-mode: both;
  489. }
  490. .animated.faster {
  491. -webkit-animation-duration: 0.5s;
  492. animation-duration: 0.5s;
  493. }
  494. @media (prefers-reduced-motion) {
  495. .animated {
  496. -webkit-animation: unset !important;
  497. -webkit-transition: none !important;
  498. animation: unset !important;
  499. transition: none !important;
  500. }
  501. }
  502. </style>