AlbumPost.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <template>
  2. <div class="album-page-container">
  3. <header class="album-header-card">
  4. <div v-if="albumInfo" class="album-info-wrapper">
  5. <div class="album-cover-box">
  6. <el-image lazy fit="cover" class="header-cover-img" :src="albumInfo.coverUrl" />
  7. </div>
  8. <div class="album-detail-content">
  9. <div class="top-row">
  10. <h2 class="album-title">{{ albumInfo.title || '播放列表' }}</h2>
  11. <el-button type="primary" size="small" icon="el-icon-video-play" class="play-all-btn" @click="onPlayAlbum">播放全部</el-button>
  12. </div>
  13. <div class="filter-row">
  14. <div class="select-group">
  15. <span class="label hidden-xs-only">切换列表:</span>
  16. <el-select
  17. v-model="queryInfo.albumId"
  18. size="small"
  19. class="album-select"
  20. @change="onSelectChange"
  21. >
  22. <el-option
  23. v-for="(item, index) in allAlbums"
  24. :key="index"
  25. :label="item.label"
  26. :value="item.value"
  27. />
  28. </el-select>
  29. </div>
  30. <div class="stats-tags">
  31. <el-tag size="mini" effect="plain" type="info">{{ albumInfo.total }} 个稿件</el-tag>
  32. <el-tag size="mini" effect="plain" type="success" class="mgl-8">{{ albumInfo.scopeStr }}</el-tag>
  33. </div>
  34. </div>
  35. </div>
  36. </div>
  37. </header>
  38. <main class="album-main-content">
  39. <div v-if="dataList.length === 0" class="empty-status">
  40. <el-empty description="暂无收藏内容" :image-size="150"></el-empty>
  41. </div>
  42. <el-row :gutter="20" class="video-grid">
  43. <el-col
  44. v-for="(item, index) in dataList"
  45. :key="index"
  46. :xs="12" :sm="8" :md="6" :lg="4" :xl="4"
  47. class="video-col"
  48. >
  49. <el-card :body-style="{ padding: '0px' }" class="video-card" shadow="hover">
  50. <div class="card-image-box">
  51. <router-link :to="`/video/${item.videoId}`" target="_blank">
  52. <el-image lazy fit="cover" class="card-cover" :src="item.coverUrl" />
  53. <div class="card-badges">
  54. <span class="duration">{{ item.duration }}</span>
  55. <span class="device-icon">
  56. <i :class="item.horizontal ? 'el-icon-monitor' : 'el-icon-mobile-phone'" />
  57. </span>
  58. </div>
  59. </router-link>
  60. </div>
  61. <div class="card-info">
  62. <router-link :to="`/video/${item.videoId}`" target="_blank" class="video-title-link">
  63. <h4 class="video-title">{{ item.title }}</h4>
  64. </router-link>
  65. <div class="video-meta">
  66. <router-link :to="`/user/${item.user.userId}`" target="_blank" class="user-link">
  67. <i class="el-icon-user" /> {{ item.user.screenName }}
  68. </router-link>
  69. <span class="pub-date">{{ item.pubDateStr }}</span>
  70. </div>
  71. <div class="card-btns">
  72. <el-button-group class="full-width-btns">
  73. <el-tooltip content="设为封面" placement="top" :disabled="screenWidth < 768">
  74. <el-button size="mini" icon="el-icon-picture-outline" @click="onSetCover(item)"></el-button>
  75. </el-tooltip>
  76. <el-tooltip content="删除收藏" placement="top" :disabled="screenWidth < 768">
  77. <el-button size="mini" type="danger" plain icon="el-icon-delete" @click="onDeleteItem(item)"></el-button>
  78. </el-tooltip>
  79. </el-button-group>
  80. </div>
  81. </div>
  82. </el-card>
  83. </el-col>
  84. </el-row>
  85. <div class="pagination-wrapper">
  86. <el-pagination
  87. background
  88. :small="screenWidth < 768"
  89. layout="total, prev, pager, next"
  90. :page-size="pageSize"
  91. :current-page="currentPage"
  92. :total="totalSize"
  93. @current-change="handleCurrentChange"
  94. />
  95. </div>
  96. </main>
  97. <el-backtop target=".admin-main"></el-backtop>
  98. </div>
  99. </template>
  100. <script>
  101. import { collectItem, getAlbumItems, getUserAlbumSelectList } from '@/api/collect'
  102. export default {
  103. name: 'AlbumPost',
  104. data() {
  105. return {
  106. allAlbums: [],
  107. queryInfo: { albumId: null, pn: 1 },
  108. screenWidth: document.body.clientWidth,
  109. currentPage: 1,
  110. pageSize: 12,
  111. totalSize: 0,
  112. dataList: [],
  113. albumInfo: null
  114. }
  115. },
  116. created() {
  117. this.getSelectList()
  118. const { albumId, pn } = this.$route.query
  119. if (albumId) this.queryInfo.albumId = albumId
  120. if (pn) {
  121. this.queryInfo.pn = parseInt(pn)
  122. this.currentPage = parseInt(pn)
  123. }
  124. this.getData()
  125. },
  126. mounted() {
  127. window.addEventListener('resize', this.handleResize)
  128. },
  129. beforeDestroy() {
  130. window.removeEventListener('resize', this.handleResize)
  131. },
  132. methods: {
  133. handleResize() {
  134. this.screenWidth = document.body.clientWidth
  135. },
  136. onSelectChange() {
  137. this.queryInfo.pn = 1
  138. this.currentPage = 1
  139. this.updateRouteAndData()
  140. },
  141. handleCurrentChange(page) {
  142. this.currentPage = page
  143. this.queryInfo.pn = page
  144. this.updateRouteAndData()
  145. const container = document.querySelector('.admin-main')
  146. if (container) container.scrollTo({ top: 0, behavior: 'smooth' })
  147. },
  148. updateRouteAndData() {
  149. this.$router.push({ path: '/bg/my/album', query: this.queryInfo })
  150. this.getData()
  151. },
  152. getData() {
  153. getAlbumItems(this.queryInfo).then(resp => {
  154. if (resp.code === 0) {
  155. this.dataList = resp.data.pageList.list
  156. this.totalSize = resp.data.pageList.totalSize
  157. this.albumInfo = resp.data.albumInfo
  158. this.queryInfo.albumId = this.albumInfo.albumId
  159. }
  160. })
  161. },
  162. getSelectList() {
  163. getUserAlbumSelectList().then(resp => {
  164. if (resp.code === 0) this.allAlbums = resp.data
  165. })
  166. },
  167. onSetCover(item) {
  168. collectItem({ albumId: this.albumInfo.albumId, postId: item.videoId, action: 3 }).then(res => {
  169. if (res.code === 0) {
  170. this.$message.success('已更新封面')
  171. this.getData()
  172. }
  173. })
  174. },
  175. onDeleteItem(item) {
  176. this.$confirm('确定移除该收藏吗?', '提示', { type: 'warning' }).then(() => {
  177. collectItem({ albumId: this.albumInfo.albumId, postId: item.videoId, action: 2 }).then(res => {
  178. if (res.code === 0) {
  179. this.$message.success('已移除')
  180. this.getData()
  181. }
  182. })
  183. }).catch(() => {})
  184. },
  185. onPlayAlbum() { this.$message.info('播放列表功能建设中') }
  186. }
  187. }
  188. </script>
  189. <style scoped>
  190. /* 1. 基础容器与布局修复 */
  191. .album-page-container {
  192. padding: 0;
  193. max-width: 1400px; /* 限制 PC 端最大宽度,防止卡片过稀疏 */
  194. margin: 0 auto;
  195. }
  196. .album-header-card {
  197. background: #fff;
  198. border-radius: 8px;
  199. padding: 24px;
  200. margin-bottom: 20px;
  201. box-shadow: 0 2px 12px rgba(0,0,0,0.04);
  202. }
  203. .album-info-wrapper {
  204. display: flex;
  205. gap: 24px;
  206. }
  207. .album-cover-box {
  208. width: 180px;
  209. height: 110px;
  210. flex-shrink: 0;
  211. border-radius: 6px;
  212. overflow: hidden;
  213. }
  214. .header-cover-img { width: 100%; height: 100%; }
  215. .album-detail-content { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: space-between; }
  216. .top-row { display: flex; justify-content: space-between; align-items: flex-start; }
  217. .album-title { margin: 0; font-size: 20px; color: #303133; font-weight: 600; }
  218. .filter-row { display: flex; align-items: center; justify-content: space-between; margin-top: 15px; }
  219. .select-group { display: flex; align-items: center; }
  220. .album-select { width: 200px; }
  221. .mgl-8 { margin-left: 8px; }
  222. /* 2. 视频卡片 PC 端优化 */
  223. .video-grid { margin: 0 -10px; } /* 抵消 col 的 padding */
  224. .video-card {
  225. border-radius: 8px;
  226. border: 1px solid #ebeef5;
  227. margin-bottom: 20px;
  228. transition: all 0.3s ease;
  229. }
  230. .card-image-box {
  231. position: relative;
  232. width: 100%;
  233. padding-top: 56.25%; /* 16:9 比例锁定 */
  234. }
  235. .card-cover {
  236. position: absolute;
  237. top: 0; left: 0;
  238. width: 100%; height: 100%;
  239. }
  240. .card-badges {
  241. position: absolute;
  242. bottom: 6px; left: 0; right: 0;
  243. padding: 0 8px;
  244. display: flex;
  245. justify-content: space-between;
  246. background: linear-gradient(transparent, rgba(0,0,0,0.6));
  247. color: #fff; font-size: 12px;
  248. }
  249. .card-info { padding: 12px; }
  250. .video-title {
  251. margin: 0 0 10px 0;
  252. font-size: 14px;
  253. height: 40px; /* 两行高度固定 */
  254. line-height: 20px;
  255. color: #303133;
  256. display: -webkit-box;
  257. -webkit-line-clamp: 2;
  258. -webkit-box-orient: vertical;
  259. overflow: hidden;
  260. }
  261. .video-meta {
  262. display: flex;
  263. justify-content: space-between;
  264. font-size: 12px;
  265. color: #909399;
  266. margin-bottom: 12px;
  267. }
  268. .user-link { color: #606266; text-decoration: none; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  269. .user-link:hover { color: #409EFF; }
  270. /* 按钮组 */
  271. .full-width-btns { display: flex; width: 100%; }
  272. .full-width-btns .el-button { flex: 1; }
  273. .pagination-wrapper { padding: 40px 0; display: flex; justify-content: center; }
  274. /* 3. 移动端专项适配修复溢出 */
  275. @media screen and (max-width: 768px) {
  276. .album-header-card { padding: 15px; margin-bottom: 15px; }
  277. .album-info-wrapper { flex-direction: column; gap: 12px; align-items: center; text-align: center; }
  278. .album-cover-box { width: 140px; height: 85px; }
  279. .top-row { flex-direction: column; align-items: center; gap: 10px; }
  280. .album-title { font-size: 16px; }
  281. .filter-row { flex-direction: column; gap: 10px; align-items: stretch; }
  282. .album-select { width: 100%; }
  283. .video-grid { margin: 0 -5px; }
  284. .video-col { padding: 0 5px !important; }
  285. .video-title { font-size: 13px; height: 36px; line-height: 18px; margin-bottom: 6px; }
  286. .video-meta { flex-direction: column; gap: 4px; margin-bottom: 8px; }
  287. .card-info { padding: 8px; }
  288. }
  289. /* 隐藏辅助类 */
  290. @media screen and (max-width: 767px) {
  291. .hidden-xs-only { display: none !important; }
  292. }
  293. </style>