video.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. <template>
  2. <div v-if="videoData !== null">
  3. <v-container>
  4. <v-col>
  5. <v-row>
  6. <v-col>
  7. <h3 v-text="videoData.title" />
  8. </v-col>
  9. </v-row>
  10. <v-row>
  11. <v-col style="color: #999;font-size: 12px;padding-top: 0px;">
  12. <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
  13. <span v-text="videoData.viewCount" /> 次观看 <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
  14. <span v-text="TimeUtil.renderTime(videoData.pubDate)" />
  15. </v-col>
  16. </v-row>
  17. </v-col>
  18. </v-container>
  19. <v-container>
  20. <v-row>
  21. <v-col>
  22. <VideoPlayer v-if="vidProp !== null" :video-prop="vidProp" />
  23. </v-col>
  24. <v-col>
  25. <v-card
  26. light
  27. >
  28. <v-card-title>
  29. <span>合集 </span>
  30. <v-switch
  31. v-model="switch1"
  32. :label="`自动播放`"
  33. />
  34. </v-card-title>
  35. <v-virtual-scroll
  36. :bench="benched"
  37. :items="videoCollection"
  38. height="300"
  39. item-height="64"
  40. >
  41. <template v-slot:default="{ item }">
  42. <v-list-item :key="item.videoId">
  43. <v-list-item-content>
  44. <v-list-item-title>
  45. <p class="text-over" style="font-size: 15px; margin-bottom: 0px;color: black;">
  46. <router-link :to="`/video/${item.videoId}`" style="color: black;"> {{ item.title }} </router-link>
  47. </p>
  48. </v-list-item-title>
  49. </v-list-item-content>
  50. </v-list-item>
  51. <v-divider />
  52. </template>
  53. </v-virtual-scroll>
  54. </v-card>
  55. </v-col>
  56. </v-row>
  57. </v-container>
  58. <v-container fill-height style="padding-top: 0px;">
  59. <v-row v-resize="onResize" no-gutters>
  60. <v-col :cols="colsWidth">
  61. <v-row>
  62. <v-col cols="2">
  63. <v-btn icon @click="likeVideo">
  64. <v-icon>mdi-thumb-up</v-icon>
  65. <span>点赞(10000)</span>
  66. </v-btn>
  67. </v-col>
  68. <v-col cols="2">
  69. <v-btn icon @click="collectVideoWrapper">
  70. <v-icon>mdi-bookmark</v-icon>
  71. <span>收藏(10000)</span>
  72. </v-btn>
  73. </v-col>
  74. <v-col cols="2">
  75. <v-btn icon @click="openRepostDialog">
  76. <v-icon>mdi-repeat</v-icon>
  77. <span>转发(10000)</span>
  78. </v-btn>
  79. <v-dialog
  80. v-model="showRepostDialog"
  81. persistent
  82. max-width="600px"
  83. >
  84. <v-card>
  85. <v-card-text>
  86. <span>https://bili.reghao.cn/video/lajfda</span>
  87. </v-card-text>
  88. <v-card-actions>
  89. <v-spacer />
  90. <v-btn
  91. color="blue darken-1"
  92. text
  93. @click="showRepostDialog = false"
  94. >
  95. 关闭
  96. </v-btn>
  97. </v-card-actions>
  98. </v-card>
  99. </v-dialog>
  100. </v-col>
  101. <v-col cols="2">
  102. <v-btn icon @click="openSuggestDialog">
  103. <v-icon>mdi-thumb-up</v-icon>
  104. <span>反馈</span>
  105. </v-btn>
  106. <v-dialog
  107. v-model="showDialog"
  108. persistent
  109. max-width="600px"
  110. >
  111. <v-card>
  112. <v-card-title>
  113. <span class="text-h5">问题或建议</span>
  114. </v-card-title>
  115. <v-card-text>
  116. <v-container>
  117. <v-row>
  118. <v-col
  119. cols="12"
  120. sm="6"
  121. >
  122. <v-select
  123. :items="['视频封面', '视频播放', '视频内容']"
  124. label="问题分类"
  125. required
  126. />
  127. </v-col>
  128. <v-col
  129. cols="24"
  130. sm="6"
  131. md="4"
  132. >
  133. <v-text-field
  134. label="问题或建议"
  135. required
  136. />
  137. </v-col>
  138. </v-row>
  139. </v-container>
  140. </v-card-text>
  141. <v-card-actions>
  142. <v-spacer />
  143. <v-btn
  144. color="blue darken-1"
  145. text
  146. @click="showDialog = false"
  147. >
  148. 关闭
  149. </v-btn>
  150. <v-btn
  151. color="blue darken-1"
  152. text
  153. @click="submitVideoErr"
  154. >
  155. 提交
  156. </v-btn>
  157. </v-card-actions>
  158. </v-card>
  159. </v-dialog>
  160. </v-col>
  161. <v-col cols="2">
  162. <v-btn icon @click="openEditDialog">
  163. <v-icon>mdi-thumb-up</v-icon>
  164. <span>编辑</span>
  165. </v-btn>
  166. <v-dialog
  167. v-model="showEditDialog"
  168. persistent
  169. max-width="600px"
  170. >
  171. <v-form
  172. ref="form"
  173. v-model="editVideo"
  174. lazy-validation
  175. >
  176. <v-text-field
  177. v-model="videoId"
  178. label="视频 ID"
  179. required
  180. />
  181. <v-text-field
  182. v-model="editVideo.pubDate"
  183. label="发布日期"
  184. required
  185. />
  186. <v-btn
  187. color="success"
  188. class="mr-4"
  189. @click="submitEdit"
  190. >
  191. 提交
  192. </v-btn>
  193. <v-btn
  194. color="error"
  195. class="mr-4"
  196. @click="resetEdit"
  197. >
  198. 重置
  199. </v-btn>
  200. <v-btn
  201. color="error"
  202. class="mr-4"
  203. @click="showEditDialog = false"
  204. >
  205. 取消
  206. </v-btn>
  207. </v-form>
  208. </v-dialog>
  209. </v-col>
  210. </v-row>
  211. <!--<v-row>
  212. <v-col style="color: #999;font-size: 12px;padding-top: 0px;">
  213. &lt;!&ndash;
  214. TODO 视频分类待完成
  215. <router-link v-if="videoData.childrenCategory.fatherId !== 0" :to="`/v/${videoData.fatherCategory.id}`" class="category-link">
  216. <span v-text="videoData.fatherCategory.name" />
  217. </router-link>
  218. /
  219. <router-link :to="`/v/${videoData.childrenCategory.id}`" class="category-link">
  220. <span v-text="videoData.childrenCategory.name" />
  221. </router-link>&ndash;&gt;
  222. <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
  223. </v-col>
  224. </v-row>-->
  225. <v-divider />
  226. <v-row>
  227. <v-col>
  228. <span v-text="videoData.description" />
  229. </v-col>
  230. </v-row>
  231. <v-divider />
  232. <v-row>
  233. <v-col>
  234. <span>{{ videoData.tags }}</span>
  235. <!-- <span v-for="item in videoData.tags" :key="item">
  236. <v-btn rounded small text color="primary" dark @click="jumpToTagPage(item)">{{ item }}</v-btn>
  237. </span>-->
  238. </v-col>
  239. </v-row>
  240. <v-divider />
  241. <v-row>
  242. <v-col cols="2" align-self="end">
  243. <router-link :to="`/u/${videoData.userId}`">
  244. <v-avatar size="48">
  245. <v-img :src="videoData.avatarUrl" />
  246. </v-avatar>
  247. </router-link>
  248. </v-col>
  249. <v-col>
  250. <router-link :to="`/u/${videoData.userId}`">
  251. <span v-text="videoData.username" />
  252. </router-link>
  253. <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
  254. <v-btn small outlined color="primary" @click="followUserWrapper">
  255. <span v-if="isFollowed">已关注</span>
  256. <span v-if="!isFollowed">关注</span>
  257. <span v-html="'&nbsp;&nbsp;'" />
  258. <span v-text="videoData.followerCount" />
  259. </v-btn>
  260. </v-col>
  261. </v-row>
  262. </v-col>
  263. </v-row>
  264. <v-row>
  265. <v-col :cols="colsWidth">
  266. <CommentCard v-if="showComment === true" :video="videoData" />
  267. </v-col>
  268. <v-col>
  269. <v-row>
  270. <span>接下来播放: </span>
  271. <v-switch
  272. v-model="switch1"
  273. :label="`自动播放`"
  274. />
  275. </v-row>
  276. <v-row no-gutters>
  277. <v-col
  278. v-for="item in videoList"
  279. :key="item.videoId"
  280. >
  281. <item-card :video="item" />
  282. </v-col>
  283. </v-row>
  284. </v-col>
  285. </v-row>
  286. </v-container>
  287. <v-dialog
  288. v-model="showUnfollowDialog"
  289. persistent
  290. max-width="600px"
  291. >
  292. <v-card>
  293. <v-card-title>
  294. <span class="text-h5">已关注, 确定要取消关注?</span>
  295. </v-card-title>
  296. <v-card-actions>
  297. <v-spacer />
  298. <v-btn
  299. color="blue darken-1"
  300. text
  301. @click="showUnfollowDialog = false"
  302. >
  303. 取消
  304. </v-btn>
  305. <v-btn
  306. color="blue darken-1"
  307. text
  308. @click="unfollowUserWrapper"
  309. >
  310. 确定
  311. </v-btn>
  312. </v-card-actions>
  313. </v-card>
  314. </v-dialog>
  315. <v-snackbar
  316. v-model="showMessage"
  317. :top="true"
  318. :timeout="1000"
  319. >
  320. {{ message }}
  321. <template v-slot:action="{ attrs }">
  322. <v-btn
  323. color="pink"
  324. text
  325. v-bind="attrs"
  326. @click="showMessage = false"
  327. >
  328. 关闭
  329. </v-btn>
  330. </template>
  331. </v-snackbar>
  332. </div>
  333. </template>
  334. <script>
  335. import { similarVideo, videoInfo, getDisplayedVideoList } from '@/api/media/video'
  336. import { collectVideo } from '@/api/media/collection'
  337. import { followUser, unfollowUser, checkRelation } from '@/api/user/user'
  338. import ItemCard from '@/components/card/item-card.vue'
  339. import CommentCard from '@/components/card/comment-card.vue'
  340. import VideoPlayer from '@/components/player/player.vue'
  341. import TimeUtil from '@/utils/time-util.vue'
  342. export default {
  343. name: 'Video',
  344. components: {
  345. ItemCard,
  346. CommentCard,
  347. VideoPlayer
  348. },
  349. data() {
  350. return {
  351. videoList: [],
  352. videoCollection: [],
  353. score: 0,
  354. TimeUtil,
  355. videoId: '',
  356. videoData: null,
  357. windowSize: {
  358. },
  359. colsWidth: 8,
  360. showDialog: false,
  361. showRepostDialog: false,
  362. formData: {},
  363. collectionDialog: false,
  364. isCollected: '收藏',
  365. showEditDialog: false,
  366. editVideo: {
  367. videoId: null,
  368. pubDate: null
  369. },
  370. showComment: true,
  371. vidProp: null,
  372. benched: 0,
  373. switch1: true,
  374. isFollowed: false,
  375. showUnfollowDialog: false,
  376. userId: null,
  377. showMessage: false,
  378. message: ''
  379. }
  380. },
  381. computed: {
  382. items() {
  383. return Array.from({ length: this.length }, (k, v) => v + 1)
  384. },
  385. length() {
  386. return 10
  387. }
  388. },
  389. created() {
  390. // 获取 url 上的 path 参数
  391. this.videoId = this.$route.params.id
  392. // 请求后端获取数据
  393. this.getVideoInfo(this.videoId)
  394. this.onResize()
  395. this.getSimilarVideos(this.videoId)
  396. this.getDisplayedVideoListWrapper()
  397. },
  398. mounted() {
  399. },
  400. methods: {
  401. // 控制页面大小
  402. onResize() {
  403. this.windowSize = { x: window.innerWidth, y: window.innerHeight }
  404. if (this.windowSize.x < 900) {
  405. this.colsWidth = 12
  406. } else {
  407. this.colsWidth = 8
  408. }
  409. },
  410. // 获取视频的详细信息
  411. getVideoInfo(videoId) {
  412. videoInfo(videoId)
  413. .then(res => {
  414. if (res.code === 0) {
  415. const vidData = res.data
  416. this.videoData = vidData
  417. document.title = vidData.title
  418. this.userId = res.data.userId
  419. this.checkRelationWrapper(this.userId)
  420. const vidProp = {}
  421. vidProp.videoId = vidData.videoId
  422. vidProp.coverUrl = vidData.coverUrl
  423. vidProp.scope = vidData.scope
  424. this.vidProp = vidProp
  425. } else {
  426. console.error(res.msg)
  427. }
  428. })
  429. .catch(error => {
  430. console.error(error.message)
  431. })
  432. },
  433. // 获取和当前视频类似的其他视频
  434. getSimilarVideos(videoId) {
  435. similarVideo(videoId)
  436. .then(res => {
  437. if (res.code === 0) {
  438. this.videoList = res.data
  439. } else {
  440. console.error(res.msg)
  441. }
  442. })
  443. .catch(error => {
  444. console.error(error.message)
  445. })
  446. },
  447. getDisplayedVideoListWrapper() {
  448. getDisplayedVideoList()
  449. .then(res => {
  450. if (res.code === 0) {
  451. this.videoCollection = res.data
  452. } else {
  453. console.error(res.msg)
  454. }
  455. })
  456. .catch(error => {
  457. console.error(error.message)
  458. })
  459. },
  460. checkRelationWrapper(userId) {
  461. checkRelation(userId).then(res => {
  462. if (res.code === 0) {
  463. this.isFollowed = res.data
  464. } else {
  465. alert(res.data)
  466. }
  467. })
  468. },
  469. followUserWrapper() {
  470. console.log(this.isFollowed)
  471. if (this.isFollowed) {
  472. this.showUnfollowDialog = true
  473. return
  474. }
  475. followUser(this.userId).then(res => {
  476. if (res.code === 0) {
  477. this.isFollowed = true
  478. this.message = '已关注'
  479. this.showMessage = true
  480. }
  481. })
  482. },
  483. unfollowUserWrapper() {
  484. if (!this.isFollowed) {
  485. return
  486. }
  487. this.showUnfollowDialog = false
  488. unfollowUser(this.userId).then(res => {
  489. if (res.code === 0) {
  490. this.isFollowed = false
  491. this.message = '已取消关注'
  492. this.showMessage = true
  493. }
  494. })
  495. },
  496. likeVideo() {
  497. console.log('点赞 ' + this.videoId)
  498. },
  499. collectVideoWrapper() {
  500. const obj = {}
  501. obj.videoId = this.videoId
  502. collectVideo(obj)
  503. .then(res => {
  504. if (res.code === 0) {
  505. this.showMessage = true
  506. this.message = '已收藏'
  507. } else {
  508. this.showMessage = true
  509. this.message = res.msg
  510. }
  511. })
  512. .catch(error => {
  513. console.error(error.message)
  514. })
  515. },
  516. jumpToTagPage(value) {
  517. console.log('跳转到标签页: ' + value)
  518. this.$router.push({
  519. path: '/tag/result',
  520. query: {
  521. tag: value,
  522. page: 1
  523. }
  524. })
  525. },
  526. openRepostDialog() {
  527. this.showRepostDialog = true
  528. console.log('获取一个 short url')
  529. },
  530. openSuggestDialog() {
  531. this.showDialog = true
  532. },
  533. openEditDialog() {
  534. this.showEditDialog = true
  535. },
  536. submitVideoErr() {
  537. this.showDialog = false
  538. console.log('提交视频错误')
  539. },
  540. submitEdit() {
  541. console.log('提交编辑信息')
  542. },
  543. resetEdit() {
  544. console.log('重置编辑信息')
  545. }
  546. }
  547. }
  548. </script>
  549. <style>
  550. </style>