Преглед изворни кода

使用 gemini 优化播放列表页面 PlaylistIndex.vue 和播放列表播放页面 PlaylistView.vue 的 UI

reghao пре 18 часа
родитељ
комит
fcc105dbfd
2 измењених фајлова са 394 додато и 501 уклоњено
  1. 172 128
      src/views/home/PlaylistIndex.vue
  2. 222 373
      src/views/home/PlaylistView.vue

+ 172 - 128
src/views/home/PlaylistIndex.vue

@@ -1,55 +1,54 @@
 <template>
-  <el-row style="padding-right: 5px; padding-left: 5px; padding-top: 10px; padding-bottom: 5px">
-    <el-col :md="24">
-      <div v-if="dataList.length === 0" align="center">
-        <img src="@/assets/img/not-result.png">
-        <div>还没有数据呢~</div>
-      </div>
-      <div v-if="dataList.length !== 0">
+  <div class="playlist-container">
+    <el-row :gutter="12" class="playlist-row">
+      <el-col :span="24" v-if="dataList.length === 0" class="empty-box">
+        <el-empty description="还没有播放列表呢~" :image-size="160"></el-empty>
+      </el-col>
+
+      <template v-else>
         <el-col
-          v-for="(item, index) in dataList"
-          :key="index"
-          :md="6"
-          :sm="12"
-          :xs="12"
-          style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px"
+            v-for="(item, index) in dataList"
+            :key="index"
+            :xs="12" :sm="8" :md="6" :lg="4"
+            class="playlist-col"
         >
-          <el-card :body-style="{ padding: '0px' }" class="card">
-            <div class="imgs" style="cursor: pointer" :title="item.albumName">
-              <router-link target="_blank" :to="`/playlist/${item.albumId}`">
+          <el-card :body-style="{ padding: '0px' }" class="playlist-card" shadow="hover">
+            <router-link :to="`/playlist/${item.albumId}`" target="_blank" class="card-link">
+              <div class="image-wrapper">
                 <el-image
-                  lazy
-                  fit="cover"
-                  class="coverImg"
-                  :src="item.coverUrl"
-                />
-                <span style="position: absolute; top: 0; left: 60%; color:white"> {{ item.duration }} </span>
-                <span style="position: absolute; bottom: 0; left: 0; color:white">
-                  <i v-if="item.horizontal" class="el-icon-monitor" />
-                  <i v-else class="el-icon-mobile-phone" />
-                </span>
-                <span style="position: absolute; bottom: 0; left: 10%; color:white">
-                  <i class="el-icon-video-play">{{ getVisited(item.view) }}</i>
-                </span>
-                <span style="position: absolute; bottom: 0; left: 40%; color:white">
-                  <i class="el-icon-s-comment">{{ getVisited(item.comment) }}</i>
-                </span>
-              </router-link>
-            </div>
-            <div style="padding: 14px">
-              <router-link style="text-decoration-line: none" target="_blank" :to="`/playlist/${item.albumId}`">
-                <span style="left: 0;margin-bottom: 0px;color: black;">{{ item.albumName | ellipsis }}</span>
-              </router-link>
-            </div>
-            <!--              <div style="padding: 14px">
-                            <span style="left: 0;margin-bottom: 0px;color: black;">
-                              <router-link target="_blank" :to="`/user/${item.user.userId}`">
-                                <i class="el-icon-user"> {{ item.user.screenName | ellipsisUsername }} </i></router-link> • {{ item.pubDateStr }}
-                            </span>
-                          </div>-->
+                    lazy
+                    fit="cover"
+                    class="playlist-cover"
+                    :src="item.coverUrl"
+                >
+                  <div slot="placeholder" class="image-slot">加载中...</div>
+                </el-image>
+
+                <div class="badge-top-right">{{ item.duration }}</div>
+
+                <div class="image-overlay">
+                  <div class="overlay-left">
+                    <span><i class="el-icon-video-play"></i> {{ getVisited(item.view) }}</span>
+                    <span><i class="el-icon-chat-dot-round"></i> {{ getVisited(item.comment) }}</span>
+                  </div>
+                  <div class="overlay-right">
+                    <i :class="item.horizontal ? 'el-icon-monitor' : 'el-icon-mobile-phone'"></i>
+                  </div>
+                </div>
+              </div>
+
+              <div class="playlist-info">
+                <h4 class="playlist-title">{{ item.albumName }}</h4>
+              </div>
+            </router-link>
           </el-card>
         </el-col>
-        <el-pagination
+      </template>
+    </el-row>
+
+    <div class="pagination-footer">
+      <el-pagination
+          background
           :small="screenWidth <= 768"
           hide-on-single-page
           layout="prev, pager, next"
@@ -57,12 +56,9 @@
           :current-page="currentPage"
           :total="totalSize"
           @current-change="handleCurrentChange"
-          @prev-click="handleCurrentChange"
-          @next-click="handleCurrentChange"
-        />
-      </div>
-    </el-col>
-  </el-row>
+      />
+    </div>
+  </div>
 </template>
 
 <script>
@@ -71,37 +67,13 @@ import { handleVisited } from '@/assets/js/utils'
 
 export default {
   name: 'PostAlbumIndex',
-  filters: {
-    ellipsis(value) {
-      if (!value) return ''
-      const max = 20
-      if (value.length > max) {
-        return value.slice(0, max) + '...'
-      }
-      return value
-    },
-    ellipsisUsername(value) {
-      if (!value) return ''
-      const max = 10
-      if (value.length > max) {
-        return value.slice(0, max) + '...'
-      }
-      return value
-    }
-  },
   data() {
     return {
-      // 屏幕宽度, 为了控制分页条的大小
       screenWidth: document.body.clientWidth,
       currentPage: 1,
       pageSize: 12,
       totalSize: 0,
       dataList: [],
-      queryInfo: {
-        postType: null,
-        pn: 1
-      },
-      showEmpty: true
     }
   },
   created() {
@@ -109,34 +81,23 @@ export default {
     this.getData()
   },
   mounted() {
-    // 当窗口宽度改变时获取屏幕宽度
     window.onresize = () => {
-      return () => {
-        window.screenWidth = document.body.clientWidth
-        this.screenWidth = window.screenWidth
-      }
+      this.screenWidth = document.body.clientWidth
     }
   },
   methods: {
     handleCurrentChange(pageNumber) {
       this.currentPage = pageNumber
       this.getData()
-      // 回到顶部
-      scrollTo(0, 0)
+      window.scrollTo({ top: 0, behavior: 'smooth' })
     },
     getData() {
-      this.dataList = []
       getPlaylist(this.currentPage).then(resp => {
         if (resp.code === 0) {
           this.dataList = resp.data.list
           this.totalSize = resp.data.totalSize
         } else {
-          this.$notify({
-            title: '提示',
-            message: resp.msg,
-            type: 'warning',
-            duration: 3000
-          })
+          this.$message.warning(resp.msg)
         }
       })
     },
@@ -148,67 +109,150 @@ export default {
 </script>
 
 <style scoped>
-/*处于手机屏幕时*/
-@media screen and (max-width: 768px){
-  .movie-list {
-    padding-top: 8px;
-    padding-left: 0.5%;
-    padding-right: 0.5%;
-  }
+.playlist-container {
+  padding: 15px;
+  max-width: 1400px;
+  margin: 0 auto;
+}
 
-  .coverImg {
-    height: 120px !important;
-  }
+.playlist-row {
+  margin: 0 !important; /* 修正 el-row 的负 margin 导致的溢出 */
+}
+
+.playlist-col {
+  margin-bottom: 20px;
+}
+
+/* 卡片整体样式 */
+.playlist-card {
+  border: none;
+  border-radius: 8px;
+  overflow: hidden;
+  transition: transform 0.3s cubic-bezier(0.2, 0, 0, 1);
+  background: #fff;
+}
+
+.playlist-card:hover {
+  transform: translateY(-4px);
+}
+
+.card-link {
+  text-decoration: none;
 }
 
-.movie-list {
-  padding-top: 15px;
-  padding-left: 6%;
-  padding-right: 6%;
+/* 封面容器 */
+.image-wrapper {
+  position: relative;
+  width: 100%;
+  aspect-ratio: 16 / 10; /* 统一比例,防止高度不一 */
+  background: #f0f0f0;
+  overflow: hidden;
 }
 
-.coverImg {
+.playlist-cover {
   width: 100%;
-  height: 130px;
+  height: 100%;
   display: block;
 }
 
-.card {
-  margin-bottom: 20px;
-  transition: all 0.6s; /*所有属性变化在0.6秒内执行动画*/
+/* 顶部时长徽标 */
+.badge-top-right {
+  position: absolute;
+  top: 6px;
+  right: 6px;
+  background: rgba(0, 0, 0, 0.6);
+  color: #fff;
+  padding: 2px 6px;
+  border-radius: 4px;
+  font-size: 11px;
+  backdrop-filter: blur(4px);
 }
 
-.imgs {
-  position: relative;
+/* 底部数据遮罩 (玻璃拟态) */
+.image-overlay {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 32px;
+  background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 0 10px;
+  color: #fff;
+  font-size: 12px;
+}
+
+.overlay-left {
+  display: flex;
+  gap: 12px;
+}
+
+.overlay-left i {
+  margin-right: 3px;
 }
 
-#collection-list {
-  padding-left: 6%;
-  padding-right: 6%;
-  padding-top: 30px;
+/* 标题信息 */
+.playlist-info {
+  padding: 12px 10px;
 }
 
-.bread {
-  font-size: 15px;
+.playlist-title {
+  margin: 0;
+  font-size: 14px;
+  color: #303133;
+  line-height: 1.4;
+  height: 2.8em; /* 限制为两行高度 */
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  font-weight: 500;
 }
 
-.movie-list {
-  padding-top: 15px;
+.playlist-title:hover {
+  color: #409EFF;
 }
 
-.reslut {
-  color: red;
+/* 分页 */
+.pagination-footer {
+  margin-top: 30px;
+  display: flex;
+  justify-content: center;
+  padding-bottom: 20px;
 }
 
-.not-result {
+.empty-box {
   padding-top: 100px;
-  padding-bottom: 100px;
-  text-align: center;
 }
 
-.remove-slot {
-  position: absolute;
-  right: 5px;
-  bottom: 2px;
+/* 移动端适配核心 */
+@media screen and (max-width: 768px) {
+  .playlist-container {
+    padding: 8px;
+  }
+
+  .playlist-col {
+    padding-left: 6px !important;
+    padding-right: 6px !important;
+    margin-bottom: 12px;
+  }
+
+  .playlist-title {
+    font-size: 13px;
+    height: 2.6em; /* 手机端略微调小 */
+  }
+
+  .image-overlay {
+    height: 28px;
+    font-size: 10px;
+    padding: 0 6px;
+  }
+
+  .badge-top-right {
+    font-size: 10px;
+    padding: 1px 4px;
+  }
 }
 </style>

+ 222 - 373
src/views/home/PlaylistView.vue

@@ -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="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
-              <span><i class="el-icon-s-comment">{{ video.comment }}</i></span>
-              <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
-              <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>