|
|
@@ -1,145 +1,124 @@
|
|
|
<template>
|
|
|
- <el-row v-if="!permissionDenied" class="movie-list">
|
|
|
- <el-col v-if="video !== null" :md="15">
|
|
|
- <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
|
|
|
- <el-card class="box-card">
|
|
|
- <div slot="header" class="clearfix">
|
|
|
- <el-row>
|
|
|
- <router-link style="text-decoration-line: none" target="_blank" :to="`/video/${video.videoId}`">
|
|
|
- <el-button style="float: right; padding: 3px 0" type="text">
|
|
|
- 原视频
|
|
|
- </el-button>
|
|
|
+ <div class="playlist-view-container">
|
|
|
+ <el-row v-if="!permissionDenied" :gutter="10" class="main-content">
|
|
|
+ <el-col v-if="video !== null" :md="16" :sm="24" :xs="24" class="video-left-section">
|
|
|
+ <el-card class="video-card" shadow="never">
|
|
|
+ <div slot="header" class="video-header">
|
|
|
+ <div class="title-row">
|
|
|
+ <h1 class="video-title" v-html="video.title" />
|
|
|
+ <router-link target="_blank" :to="`/video/${video.videoId}`">
|
|
|
+ <el-link type="primary" :underline="false" class="origin-link">原视频 <i class="el-icon-right" /></el-link>
|
|
|
</router-link>
|
|
|
- </el-row>
|
|
|
- <el-row>
|
|
|
- <h3 v-html="video.title" />
|
|
|
- </el-row>
|
|
|
- <el-row style="color: #999;font-size: 16px;padding-top: 0px;">
|
|
|
- <span><i class="el-icon-video-play">{{ video.view }}</i></span>
|
|
|
- <span v-html="' '" />
|
|
|
- <span><i class="el-icon-s-comment">{{ video.comment }}</i></span>
|
|
|
- <span v-html="' '" />
|
|
|
- <span><i class="el-icon-watch">{{ video.pubDate }}</i></span>
|
|
|
- </el-row>
|
|
|
+ </div>
|
|
|
+ <div class="video-stats">
|
|
|
+ <span><i class="el-icon-video-play" /> {{ video.view }}</span>
|
|
|
+ <span><i class="el-icon-s-comment" /> {{ video.comment }}</span>
|
|
|
+ <span><i class="el-icon-time" /> {{ video.pubDate }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="text item">
|
|
|
- <div id="dplayer" ref="dplayer" style="height: 480px;" />
|
|
|
+
|
|
|
+ <div class="player-wrapper">
|
|
|
+ <div id="dplayer" ref="dplayer" class="dplayer-instance" />
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
- </el-row>
|
|
|
- <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
|
|
|
- <el-card class="box-card">
|
|
|
- <div slot="header" class="clearfix">
|
|
|
- <div class="video-data-row">
|
|
|
- <el-button
|
|
|
+
|
|
|
+ <div class="video-actions">
|
|
|
+ <el-button
|
|
|
type="danger"
|
|
|
- size="mini"
|
|
|
+ size="small"
|
|
|
+ round
|
|
|
icon="el-icon-collection"
|
|
|
:disabled="isCollected"
|
|
|
@click="collection(video.videoId)"
|
|
|
- >
|
|
|
- <span>收藏 {{ video.favorite }}</span>
|
|
|
- </el-button>
|
|
|
- <el-button
|
|
|
- type="danger"
|
|
|
- size="mini"
|
|
|
- icon="el-icon-delete"
|
|
|
+ >
|
|
|
+ 收藏 {{ video.favorite }}
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ type="info"
|
|
|
+ size="small"
|
|
|
+ round
|
|
|
+ plain
|
|
|
+ icon="el-icon-warning-outline"
|
|
|
@click="deleteVideo(video)"
|
|
|
- >
|
|
|
- <span>删除</span>
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
+ >
|
|
|
+ 报错/删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card class="description-card" shadow="never">
|
|
|
+ <div class="description-content">
|
|
|
+ <p class="desc-text" v-html="video.description || '暂无简介'" />
|
|
|
</div>
|
|
|
- <div class="text item">
|
|
|
- <!--视频描述行-->
|
|
|
- <span class="description" v-html="video.description" />
|
|
|
- <el-divider />
|
|
|
- <!--视频标签行-->
|
|
|
- <div class="v-tag">
|
|
|
- <el-tag
|
|
|
- v-for="(tag,index) in video.tags"
|
|
|
+ <el-divider />
|
|
|
+ <div class="tag-group">
|
|
|
+ <el-tag
|
|
|
+ v-for="(tag, index) in video.tags"
|
|
|
:key="index"
|
|
|
- class="tag"
|
|
|
- size="medium"
|
|
|
+ class="video-tag"
|
|
|
+ size="small"
|
|
|
effect="plain"
|
|
|
- >
|
|
|
- <router-link style="text-decoration-line: none" target="_blank" :to="`/video/tag/` + tag">
|
|
|
- {{ tag }}
|
|
|
- </router-link>
|
|
|
- </el-tag>
|
|
|
- </div>
|
|
|
+ >
|
|
|
+ <router-link :to="`/video/tag/${tag}`">{{ tag }}</router-link>
|
|
|
+ </el-tag>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
- </el-row>
|
|
|
- </el-col>
|
|
|
- <el-col :md="9">
|
|
|
- <el-row>
|
|
|
- <el-row v-if="showPlaylist" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
|
|
|
- <el-card class="box-card">
|
|
|
- <div slot="header" class="clearfix">
|
|
|
- <el-row>
|
|
|
- <h3>播放列表</h3>
|
|
|
- </el-row>
|
|
|
- <el-row>
|
|
|
- <span>自动播放 <el-switch v-model="autoPlay" /></span>
|
|
|
- </el-row>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :md="8" :sm="24" :xs="24" class="playlist-right-section">
|
|
|
+ <el-card class="playlist-card" shadow="never">
|
|
|
+ <div slot="header" class="playlist-header">
|
|
|
+ <div class="flex-between">
|
|
|
+ <h3 class="m0">播放列表</h3>
|
|
|
+ <div class="auto-play-switch">
|
|
|
+ <span class="label-text">自动播放</span>
|
|
|
+ <el-switch v-model="autoPlay" active-color="#13ce66" />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="text item">
|
|
|
- <el-table
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="playlist-body">
|
|
|
+ <el-table
|
|
|
:data="playList.list"
|
|
|
:show-header="false"
|
|
|
- height="480"
|
|
|
- style="width: 100%"
|
|
|
- >
|
|
|
- <el-table-column
|
|
|
- type="index"
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="coverUrl"
|
|
|
- >
|
|
|
- <template slot-scope="scope">
|
|
|
+ highlight-current-row
|
|
|
+ :row-class-name="tableRowClassName"
|
|
|
+ @row-click="playItem"
|
|
|
+ class="playlist-table"
|
|
|
+ >
|
|
|
+ <el-table-column width="40" type="index" align="center" />
|
|
|
+ <el-table-column width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="playlist-cover-container">
|
|
|
<el-image
|
|
|
- lazy
|
|
|
- fit="cover"
|
|
|
- class="coverImg"
|
|
|
- :src="scope.row.coverUrl"
|
|
|
+ lazy
|
|
|
+ fit="cover"
|
|
|
+ class="playlist-cover-img"
|
|
|
+ :src="scope.row.coverUrl"
|
|
|
/>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column
|
|
|
- prop="title"
|
|
|
- >
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-button type="text" @click="playItem(scope.row)">
|
|
|
- {{ scope.row.title | ellipsis }}
|
|
|
- </el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column
|
|
|
- prop="duration"
|
|
|
- >
|
|
|
- <template slot-scope="scope">
|
|
|
- <span>{{ scope.row.duration }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-row>
|
|
|
- </el-row>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-row v-else>
|
|
|
- <permission-denied-card :text-object="textObject" />
|
|
|
- </el-row>
|
|
|
+ <span class="duration-badge">{{ scope.row.duration }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column>
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span class="playlist-item-title">{{ scope.row.title }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row v-else>
|
|
|
+ <permission-denied-card :text-object="textObject" />
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import PermissionDeniedCard from '@/components/card/PermissionDeniedCard'
|
|
|
-
|
|
|
-import flvjs from 'flv.js'
|
|
|
import DPlayer from 'dplayer'
|
|
|
-
|
|
|
import { videoUrl, videoInfo } from '@/api/video'
|
|
|
import { videoErrorDelete } from '@/api/video_edit'
|
|
|
import { getPlaylistItems } from '@/api/collect'
|
|
|
@@ -148,323 +127,193 @@ import { getUserInfo } from '@/api/user'
|
|
|
export default {
|
|
|
name: 'PlaylistView',
|
|
|
components: { PermissionDeniedCard },
|
|
|
- filters: {
|
|
|
- ellipsis(value) {
|
|
|
- if (!value) return ''
|
|
|
- const max = 20
|
|
|
- if (value.length > max) {
|
|
|
- return value.slice(0, max) + '...'
|
|
|
- }
|
|
|
- return value
|
|
|
- }
|
|
|
- },
|
|
|
data() {
|
|
|
return {
|
|
|
video: null,
|
|
|
user: null,
|
|
|
isCollected: false,
|
|
|
- errorReportForm: {
|
|
|
- videoId: null,
|
|
|
- errorCode: null
|
|
|
- },
|
|
|
permissionDenied: false,
|
|
|
- textObject: {
|
|
|
- content: '视频',
|
|
|
- route: '/video'
|
|
|
- },
|
|
|
- showPlaylist: true,
|
|
|
+ textObject: { content: '视频', route: '/video' },
|
|
|
autoPlay: false,
|
|
|
- playList: {
|
|
|
- current: 0,
|
|
|
- list: []
|
|
|
- },
|
|
|
- // **********************************************************************/
|
|
|
- flvjs,
|
|
|
- DPlayer,
|
|
|
+ playList: { current: 0, list: [] },
|
|
|
danmaku: {
|
|
|
api: process.env.VUE_APP_SERVER_URL + '/api/comment/danmaku/',
|
|
|
token: 'tnbapp'
|
|
|
},
|
|
|
- getUrl: true
|
|
|
+ player: null
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
- // 地址栏 url 发生变化时重新加载本页面
|
|
|
- $route() {
|
|
|
- this.$router.go()
|
|
|
+ '$route.params.albumId': function() {
|
|
|
+ this.initPage()
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
- const albumId = this.$route.params.albumId
|
|
|
- getPlaylistItems(albumId).then(resp => {
|
|
|
- if (resp.code === 0) {
|
|
|
- const respData = resp.data
|
|
|
- document.title = respData.albumInfo.albumName
|
|
|
- this.playList.list = respData.pageList.list
|
|
|
-
|
|
|
- const videoId = this.playList.list[0].videoId
|
|
|
- this.getVideoInfo(videoId)
|
|
|
-
|
|
|
- const key = 'myplaylist-' + videoId
|
|
|
- const value = localStorage.getItem(key)
|
|
|
- if (value != null) {
|
|
|
- this.playList = JSON.parse(value)
|
|
|
- this.calculateCurrent(videoId)
|
|
|
- }
|
|
|
- } else {
|
|
|
- document.title = '播放列表'
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- mounted() {
|
|
|
- window.addEventListener('beforeunload', this.handleBeforeUnload)
|
|
|
- const header = this.$refs.header
|
|
|
- if (header !== undefined && header !== null) {
|
|
|
- console.log('header -> ' + header)
|
|
|
- this.wrapStyle = `height: calc(100vh - ${header.clientHeight + 20}px)`
|
|
|
- }
|
|
|
+ this.initPage()
|
|
|
},
|
|
|
methods: {
|
|
|
- handleBeforeUnload(event) {
|
|
|
- const key = 'myplaylist-' + this.video.videoId
|
|
|
- localStorage.removeItem(key)
|
|
|
- // event.preventDefault()
|
|
|
+ initPage() {
|
|
|
+ const albumId = this.$route.params.albumId
|
|
|
+ getPlaylistItems(albumId).then(resp => {
|
|
|
+ if (resp.code === 0) {
|
|
|
+ this.playList.list = resp.data.pageList.list
|
|
|
+ document.title = resp.data.albumInfo.albumName
|
|
|
+ if (this.playList.list.length > 0) {
|
|
|
+ this.getVideoInfo(this.playList.list[0].videoId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
},
|
|
|
- // 获取视频的详细信息
|
|
|
getVideoInfo(videoId) {
|
|
|
videoInfo(videoId).then(resp => {
|
|
|
if (resp.code === 0) {
|
|
|
this.video = resp.data
|
|
|
this.getVideoUrl(videoId)
|
|
|
-
|
|
|
- this.userId = resp.data.userId
|
|
|
- getUserInfo(this.userId).then(resp => {
|
|
|
- if (resp.code === 0) {
|
|
|
- this.user = resp.data
|
|
|
- } else {
|
|
|
- this.$notify.error({
|
|
|
- message: '用户数据获取失败',
|
|
|
- type: 'warning',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- } else if (resp.code === 2) {
|
|
|
- this.$router.push('/404')
|
|
|
} else {
|
|
|
this.permissionDenied = true
|
|
|
}
|
|
|
- }).catch(error => {
|
|
|
- this.$notify.error({
|
|
|
- message: error.message,
|
|
|
- type: 'warning',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
- })
|
|
|
- },
|
|
|
- // 用户点击收藏
|
|
|
- collection(videoId) {
|
|
|
- },
|
|
|
- deleteVideo(video) {
|
|
|
- this.$confirm('确定要删除 ' + video.title + '?', '提示', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'warning',
|
|
|
- customClass: 'msgbox'
|
|
|
- }).then(() => {
|
|
|
- const videoId = video.videoId
|
|
|
- const errorReportForm = {
|
|
|
- videoId: videoId,
|
|
|
- errorCode: 4
|
|
|
- }
|
|
|
-
|
|
|
- videoErrorDelete(errorReportForm).then(resp => {
|
|
|
- if (resp.code === 0) {
|
|
|
- this.errorReportForm.errorCode = null
|
|
|
- this.$notify({
|
|
|
- title: '提示',
|
|
|
- message: '视频错误已提交',
|
|
|
- type: 'warning',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
- } else {
|
|
|
- this.$notify({
|
|
|
- title: '提示',
|
|
|
- message: resp.msg,
|
|
|
- type: 'warning',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
- }
|
|
|
- }).catch(error => {
|
|
|
- this.$notify({
|
|
|
- title: '提示',
|
|
|
- message: error.message,
|
|
|
- type: 'warning',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
- })
|
|
|
- }).catch(() => {
|
|
|
- this.$message({
|
|
|
- type: 'info',
|
|
|
- message: '已取消'
|
|
|
- })
|
|
|
})
|
|
|
},
|
|
|
- // ****************************************************************************************************************
|
|
|
getVideoUrl(videoId) {
|
|
|
videoUrl(videoId).then(res => {
|
|
|
- if (res.code === 0) {
|
|
|
- const urlType = res.data.type
|
|
|
- if (urlType === 'mp4') {
|
|
|
- const urls = res.data.urls
|
|
|
- for (const url of urls) {
|
|
|
- url.type = 'normal'
|
|
|
- }
|
|
|
- this.initMp4Player(this.video.userId, videoId, this.video.coverUrl, urls, res.data.currentTime)
|
|
|
- } else {
|
|
|
- this.$notify.error({
|
|
|
- message: '视频 url 类型不合法',
|
|
|
- type: 'warning',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
- }
|
|
|
- } else {
|
|
|
- this.$notify.error({
|
|
|
- message: '视频 url 获取失败',
|
|
|
- type: 'warning',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
+ if (res.code === 0 && res.data.type === 'mp4') {
|
|
|
+ this.initPlayer(res.data.urls, res.data.currentTime)
|
|
|
}
|
|
|
- }).catch(error => {
|
|
|
- this.$notify.error({
|
|
|
- message: error.message,
|
|
|
- type: 'error',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
})
|
|
|
},
|
|
|
- initMp4Player(userId, videoId, coverUrl, urls, pos) {
|
|
|
- const player = new DPlayer({
|
|
|
- container: document.querySelector('#dplayer'),
|
|
|
- lang: 'zh-cn',
|
|
|
- screenshot: false,
|
|
|
- autoplay: false,
|
|
|
- volume: 0.1,
|
|
|
- mutex: true,
|
|
|
+ initPlayer(urls, pos) {
|
|
|
+ if (this.player) this.player.destroy()
|
|
|
+
|
|
|
+ this.player = new DPlayer({
|
|
|
+ container: this.$refs.dplayer,
|
|
|
+ autoplay: this.autoPlay,
|
|
|
+ theme: '#409EFF',
|
|
|
+ volume: 0.7,
|
|
|
video: {
|
|
|
- pic: coverUrl,
|
|
|
- defaultQuality: 0,
|
|
|
- quality: urls,
|
|
|
- hotkey: true
|
|
|
+ pic: this.video.coverUrl,
|
|
|
+ quality: urls.map(u => ({ name: u.label || '标清', url: u.url, type: 'normal' })),
|
|
|
+ defaultQuality: 0
|
|
|
},
|
|
|
danmaku: {
|
|
|
- id: videoId,
|
|
|
- maximum: 10000,
|
|
|
+ id: this.video.videoId,
|
|
|
api: this.danmaku.api,
|
|
|
token: this.danmaku.token,
|
|
|
- user: userId,
|
|
|
- bottom: '15%',
|
|
|
- unlimited: true
|
|
|
+ user: this.video.userId
|
|
|
}
|
|
|
})
|
|
|
-
|
|
|
- // 设置音量
|
|
|
- // player.volume(0.1, true, false)
|
|
|
- // 跳转到上次看到的位置
|
|
|
- player.seek(pos)
|
|
|
-
|
|
|
- /* 事件绑定 */
|
|
|
- player.on('progress', function() {
|
|
|
- })
|
|
|
-
|
|
|
- player.on('ended', () => {
|
|
|
- this.$message.info('当前视频播放完成')
|
|
|
- })
|
|
|
-
|
|
|
- player.on('volumechange', () => {
|
|
|
- console.log('声音改变')
|
|
|
+ this.player.seek(pos)
|
|
|
+ this.player.on('ended', () => {
|
|
|
+ if (this.autoPlay) this.playNext()
|
|
|
})
|
|
|
},
|
|
|
- calculateCurrent(videoId) {
|
|
|
- for (var i = 0; i < this.playList.list.length; i++) {
|
|
|
- if (videoId === this.playList.list[i].videoId) {
|
|
|
- this.playList.current = i
|
|
|
- const key = 'myplaylist-' + this.video.videoId
|
|
|
- localStorage.setItem(key, JSON.stringify(this.playList))
|
|
|
- }
|
|
|
+ playItem(row) {
|
|
|
+ this.getVideoInfo(row.videoId)
|
|
|
+ // 移动端点击后滚动到顶部播放器
|
|
|
+ if (window.innerWidth < 768) {
|
|
|
+ window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
|
}
|
|
|
},
|
|
|
- setCurrent(current) {
|
|
|
- this.playList.current = current
|
|
|
- const key = 'myplaylist-' + this.video.videoId
|
|
|
- localStorage.setItem(key, JSON.stringify(this.playList))
|
|
|
- },
|
|
|
- getNextPath(current) {
|
|
|
- this.calculateCurrent(current)
|
|
|
- const next = this.playList.current + 1
|
|
|
- if (next < this.playList.list.length) {
|
|
|
- this.setCurrent(next)
|
|
|
- const videoId = this.playList.list[next].videoId
|
|
|
- const path = '/vidlist/' + videoId
|
|
|
- if (path !== this.$route.path) {
|
|
|
- this.$router.push(path)
|
|
|
- } else {
|
|
|
- console.log(this.playList)
|
|
|
- this.$notify.info({
|
|
|
- message: '视频列表播放完成',
|
|
|
- duration: 3000
|
|
|
- })
|
|
|
- }
|
|
|
+ playNext() {
|
|
|
+ const currentIndex = this.playList.list.findIndex(i => i.videoId === this.video.videoId)
|
|
|
+ if (currentIndex < this.playList.list.length - 1) {
|
|
|
+ this.playItem(this.playList.list[currentIndex + 1])
|
|
|
}
|
|
|
},
|
|
|
- playItem(item) {
|
|
|
- this.getVideoInfo(item.videoId)
|
|
|
+ tableRowClassName({ row }) {
|
|
|
+ if (this.video && row.videoId === this.video.videoId) {
|
|
|
+ return 'current-playing-row'
|
|
|
+ }
|
|
|
+ return ''
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-/*处于手机屏幕时*/
|
|
|
-@media screen and (max-width: 768px) {
|
|
|
- .movie-list {
|
|
|
- padding-top: 8px;
|
|
|
- padding-left: 0.5%;
|
|
|
- padding-right: 0.5%;
|
|
|
- }
|
|
|
-
|
|
|
- .msgbox{
|
|
|
- width: 320px !important;
|
|
|
- }
|
|
|
+.playlist-view-container {
|
|
|
+ max-width: 1400px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 15px;
|
|
|
+ background-color: #f4f4f5;
|
|
|
+ min-height: 100vh;
|
|
|
}
|
|
|
|
|
|
-.movie-list {
|
|
|
- padding-top: 5px;
|
|
|
- padding-bottom: 5px;
|
|
|
- padding-left: 5px;
|
|
|
- padding-right: 5px;
|
|
|
-}
|
|
|
+.m0 { margin: 0; }
|
|
|
+.flex-between { display: flex; justify-content: space-between; align-items: center; }
|
|
|
|
|
|
-.clearfix:before,
|
|
|
-.clearfix:after {
|
|
|
- display: table;
|
|
|
- content: "";
|
|
|
-}
|
|
|
+/* 视频标题与信息 */
|
|
|
+.video-header { padding: 10px 0; }
|
|
|
+.title-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
|
|
|
+.video-title { font-size: 1.2rem; line-height: 1.4; margin: 0 0 10px 0; color: #1f1f1f; flex: 1; }
|
|
|
+.origin-link { font-size: 14px; white-space: nowrap; }
|
|
|
+.video-stats { color: #909399; font-size: 13px; display: flex; gap: 15px; flex-wrap: wrap; }
|
|
|
|
|
|
-.clearfix:after {
|
|
|
- clear: both;
|
|
|
+/* 播放器适配 */
|
|
|
+.player-wrapper {
|
|
|
+ background: #000;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
|
|
|
-.v-tag {
|
|
|
- padding-top: 10px;
|
|
|
+/* PC端高度,移动端通过媒体查询调整 */
|
|
|
+.dplayer-instance { height: 500px; width: 100%; }
|
|
|
+
|
|
|
+.video-actions { padding: 15px 0 5px; display: flex; gap: 10px; }
|
|
|
+
|
|
|
+/* 简介与标签 */
|
|
|
+.description-card { margin-top: 10px; border: none; }
|
|
|
+.desc-text { font-size: 14px; color: #606266; line-height: 1.6; white-space: pre-wrap; margin: 0; }
|
|
|
+.tag-group { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
|
+.video-tag a { text-decoration: none; color: inherit; }
|
|
|
+
|
|
|
+/* 播放列表 */
|
|
|
+.playlist-header { padding: 5px 0; }
|
|
|
+.auto-play-switch { display: flex; align-items: center; gap: 8px; }
|
|
|
+.label-text { font-size: 13px; color: #606266; }
|
|
|
+
|
|
|
+.playlist-body { max-height: 600px; overflow-y: auto; }
|
|
|
+.playlist-table { cursor: pointer; }
|
|
|
+
|
|
|
+.playlist-cover-container { position: relative; width: 90px; height: 56px; border-radius: 4px; overflow: hidden; }
|
|
|
+.playlist-cover-img { width: 100%; height: 100%; }
|
|
|
+.duration-badge {
|
|
|
+ position: absolute; bottom: 2px; right: 2px;
|
|
|
+ background: rgba(0,0,0,0.7); color: #fff;
|
|
|
+ font-size: 10px; padding: 0 4px; border-radius: 2px;
|
|
|
}
|
|
|
-.tag{
|
|
|
- margin-right: 3px;
|
|
|
+.playlist-item-title {
|
|
|
+ font-size: 13px; color: #303133;
|
|
|
+ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
|
|
|
}
|
|
|
|
|
|
-.coverImg {
|
|
|
- width: 100%;
|
|
|
- height: 90px;
|
|
|
- display: block;
|
|
|
+/* 移动端深度适配 */
|
|
|
+@media screen and (max-width: 768px) {
|
|
|
+ .playlist-view-container { padding: 0; background-color: #fff; }
|
|
|
+ .video-left-section, .playlist-right-section { padding: 0 !important; }
|
|
|
+
|
|
|
+ /* 移动端播放器强制 16:9 */
|
|
|
+ .dplayer-instance { height: 56.25vw !important; }
|
|
|
+
|
|
|
+ .video-card { border: none; border-radius: 0; }
|
|
|
+ .video-card ::v-deep .el-card__header { padding: 10px 15px; }
|
|
|
+ .video-card ::v-deep .el-card__body { padding: 0; }
|
|
|
+
|
|
|
+ .video-title { font-size: 1rem; }
|
|
|
+ .video-actions { padding: 10px 15px; }
|
|
|
+ .description-card { border-radius: 0; }
|
|
|
+
|
|
|
+ .playlist-card { border: none; border-top: 8px solid #f4f4f5; border-radius: 0; }
|
|
|
+ .playlist-body { max-height: none; }
|
|
|
+}
|
|
|
+
|
|
|
+/* 高亮当前播放行 */
|
|
|
+::v-deep .current-playing-row {
|
|
|
+ background-color: #ecf5ff !important;
|
|
|
+}
|
|
|
+::v-deep .current-playing-row .playlist-item-title {
|
|
|
+ color: #409EFF;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
</style>
|