Refactor video card component

This commit is contained in:
the1812 2019-09-12 10:49:17 +08:00
parent 987eed51b4
commit 6eb20d37d5
14 changed files with 342 additions and 278 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -66,7 +66,7 @@
"hide-category.min.js": "64125DCFE1F7DB269049CB839B52E38B4E2D574E7A1AF6A5439BF0B77EB93EC1",
"hide-old-entry.min.js": "82C5BB63906A244E1A8CAF2162F502A31188E8437387829B4AC9950E8836100E",
"hide-top-search.min.js": "19641CCB6A883DB5DD143A768606DEF0C571572D91CCD3FD5A9302A2A916E19B",
"home-video.vue.min.js": "D59D70D527A4F6769A5E04A760FCE6C62E8B69CDFCEA5CDEC6338D19261FE7E0",
"home-video.vue.min.js": "9CA2EF60468CF376E5FB6EB8DC55703A07C09D8197FD53AA3D43B131AE37BBAA",
"i18n.de-DE.min.js": "5BDAC5F0493F447A98B4D308817B21AB2787BEF9E3929EA37F0D2ACD3D879337",
"i18n.en-US.min.js": "9DF9263CB0D4CA8FDB17A75F6ECA0D48F8B34540947C70C4FEA0403613DD1C43",
"i18n.ja-JP.min.js": "9D924D58A6CD265EB95DA29B7E64885CD74089EC14A07C36CF3EAF42C8BEE33B",
@ -85,7 +85,7 @@
"medal-helper.min.css": "205A02CC6E8B2DBDA222A0660D83EA6E4B24F0B5A753E269FED099AB8AE31B6C",
"medal-helper.min.html": "5D7057259368BE97DED3375DD904695B245AA2BF338C5E18CB3CF61DD913617C",
"medal-helper.min.js": "33751914C5DE205D79E059F7BF285E325E4D6DBFC68AE7E4D4B6307672F23B6E",
"minimal-home.vue.min.js": "7F610F3C3812BC82005E8031B6597D7516D36BEB5FA2900B0385176D50483DCD",
"minimal-home.vue.min.js": "B22CB38E0340256B773EE9FFF7F673BE6A8ABA9B66174BCB650AF5D98B24A558",
"narrow-danmaku.min.js": "12475431A527EFF15100AF57C9C53D0603BC27723126D966CB01240917602253",
"new-styles.min.js": "3D7E8E25C5B5B6BD3F784BC05D403C35F0658E67197F878874A9554C2FF5127B",
"no-banner.min.css": "DA096F94E7FA26992F3F71245E704D69A1C222D0ADA6F1990FA5D948507CE15F",
@ -100,6 +100,7 @@
"override-navbar.min.js": "5EC6A7D1D2ADE38FEA0BDE3FBBDB5A4054A05FE2FC386FB3339CD9AD3AF7F4DE",
"player-focus.min.js": "2C849315D6FE5968908ACC2F0CD6252C8D5988485B10C1FD09C96D2E397FD30D",
"player-shadow.min.js": "918D2127907BC2C164CB86BF7F9C2501CF3B9CD236FEEDE5155B9B5D932F3415",
"rank-list.vue.min.js": "51CC28C7D4FB94D69CE02602779DE2D0793791FA8D18B2DA94BC298B98FDCC40",
"remove-promotions.min.css": "FC6AA1EE75AFD8C82E8AFBC68FD898364AD8EB3720503B1A7A613CAB2B38C5EC",
"remove-promotions.min.js": "A10C64F78511BC75B9BBA2DFFEBF7FCC4C6C505EF2F75DC0C7AFEF3E162BEA1E",
"remove-top-mask.min.js": "A15C1EC10D2E1A61845B1ADAE51860553F96427059E10443B1E53FAF48F45304",
@ -139,6 +140,7 @@
"tweets.min.css": "9015C1F165C91B9205ABBE4E8A3B5FA83DB1EB4B5CF2442846AF61E44FF178A8",
"v-checkbox.vue.min.js": "A23C35D5627009A29FD396A024442A19E37DEE70674BF3CE0FA377D781ED2231",
"v-dropdown.vue.min.js": "66C883695F6653412980050A23F7581557407A2859214BF0CAAEC7001D0B5DDB",
"video-card.vue.min.js": "22685DF32E1B294F5BB13D91E05A941A2543F4DE8B168275AAE1D62FB717A781",
"video-info.min.js": "7234A74CA28A772A31E41BBD4925FC2B22C6701419ADFD511402C6A9D6D9E5FA",
"video-story.min.js": "F9D4D2D80997DF5F11BF0CFA4D389B77BDBF1566EA6002E47174B68B440F3C0D",
"view-cover.min.js": "C458AD430FB767111D2F8E1F63BF85C3047FC7FDDED82D00C0D8EEA9542BD945",

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(()=>{return(a,e)=>{const i=`<div class=minimal-home><div class=minimal-home-header><icon class=logo icon=logo type=main></icon><div class=home-tabs><div v-for="(tab, index) of tabs":key=index class=tab :class="{active: tab.active}":data-tab=tab.name @click=changeTab(tab)>{{tab.displayName}}</div></div></div><div class=minimal-home-content><component :is=content></component></div></div>`;e.applyStyleFromText(`.minimal-home{--card-width:600px;--card-height:120px;--card-margin:16px;--card-column-count:2;transform:translateX(calc(var(--card-margin)/ 2))}.minimal-home,.minimal-home *{box-sizing:border-box;transition:.2s ease-out}.minimal-home .logo{font-size:48px;color:var(--theme-color)}.minimal-home .minimal-home-header{display:flex;align-items:center;justify-content:space-between}.minimal-home .minimal-home-header .home-tabs{display:flex;flex-grow:1;justify-content:flex-end;margin-right:var(--card-margin)}.minimal-home .minimal-home-header .home-tabs .tab{color:#707070;opacity:.75;position:relative;cursor:pointer;margin-left:32px}body.dark .minimal-home .minimal-home-header .home-tabs .tab{color:#eee}.minimal-home .minimal-home-header .home-tabs .tab.active{transform:scale(1.2);opacity:1;font-weight:700}.minimal-home .minimal-home-header .home-tabs .tab::after{content:"";position:absolute;bottom:-8px;left:50%;transform:translateX(-50%) scaleX(0);height:3px;width:24px;background-color:var(--theme-color);border-radius:2px;transition:.2s ease-out}.minimal-home .minimal-home-header .home-tabs .tab.active::after{transform:translateX(-50%) scaleX(1)}.minimal-home .minimal-home-content{margin-top:32px;width:calc(var(--card-column-count) * (var(--card-width) + var(--card-margin)))}@media screen and (max-width:1300px){.minimal-home{--card-column-count:1}}`,"minimal-home-style");return{export:Object.assign({template:i},{components:{Icon:()=>e.importAsync("icon.vue"),HomeVideo:()=>e.importAsync("home-video.vue")},data(){return{tabs:[{name:"video",displayName:"视频动态",active:true,more:"https://t.bilibili.com/?tab=8"},{name:"ranking7",displayName:"一周排行",active:false,more:"https://www.bilibili.com/ranking/all/0/0/7"},{name:"ranking3",displayName:"三日排行",active:false,more:"https://www.bilibili.com/ranking"},{name:"ranking1",displayName:"昨日排行",active:false,more:"https://www.bilibili.com/ranking/all/0/0/1"}],content:"HomeVideo"}},methods:{changeTab(a){if(a.active){window.open(a.more,"_blank");return}const e=this.tabs.find(a=>a.active);e.active=false;a.active=true}}})}}})();
(()=>{return(a,e)=>{const i=`<div class=minimal-home><div class=minimal-home-header><icon class=logo icon=logo type=main></icon><div class=home-tabs><div v-for="(tab, index) of tabs":key=index class=tab :class="{active: tab.active}":data-tab=tab.name @click=changeTab(tab)>{{tab.displayName}}</div></div></div><div class=minimal-home-content><component :is=content :key=activeTab.name :rank-days=rankDays></component></div></div>`;e.applyStyleFromText(`.minimal-home{--card-width:600px;--card-height:120px;--card-margin:16px;--card-column-count:2;transform:translateX(calc(var(--card-margin)/ 2))}.minimal-home,.minimal-home *{box-sizing:border-box;transition:.2s ease-out}.minimal-home .logo{font-size:48px;color:var(--theme-color)}.minimal-home .minimal-home-header{display:flex;align-items:center;justify-content:space-between}.minimal-home .minimal-home-header .home-tabs{display:flex;flex-grow:1;justify-content:flex-end;margin-right:var(--card-margin)}.minimal-home .minimal-home-header .home-tabs .tab{color:#707070;opacity:.75;position:relative;cursor:pointer;margin-left:32px}body.dark .minimal-home .minimal-home-header .home-tabs .tab{color:#eee}.minimal-home .minimal-home-header .home-tabs .tab.active{transform:scale(1.2);opacity:1;font-weight:700}.minimal-home .minimal-home-header .home-tabs .tab::after{content:"";position:absolute;bottom:-8px;left:50%;transform:translateX(-50%) scaleX(0);height:3px;width:24px;background-color:var(--theme-color);border-radius:2px;transition:.2s ease-out}.minimal-home .minimal-home-header .home-tabs .tab.active::after{transform:translateX(-50%) scaleX(1)}.minimal-home .minimal-home-content{margin-top:32px;width:calc(var(--card-column-count) * (var(--card-width) + var(--card-margin)))}@media screen and (max-width:1300px){.minimal-home{--card-column-count:1}}`,"minimal-home-style");const t=[{name:"video",displayName:"视频动态",active:true,more:"https://t.bilibili.com/?tab=8"},{name:"ranking7",displayName:"一周排行",active:false,more:"https://www.bilibili.com/ranking/all/0/0/7",rankDays:7},{name:"ranking3",displayName:"三日排行",active:false,more:"https://www.bilibili.com/ranking",rankDays:3},{name:"ranking1",displayName:"昨日排行",active:false,more:"https://www.bilibili.com/ranking/all/0/0/1",rankDays:1}];return{export:Object.assign({template:i},{components:{Icon:()=>e.importAsync("icon.vue"),HomeVideo:()=>e.importAsync("home-video.vue"),RankList:()=>e.importAsync("rank-list.vue")},data(){return{tabs:t,content:"HomeVideo"}},computed:{activeTab(){return this.tabs.find(a=>a.active)},rankDays(){return this.activeTab.rankDays||0}},methods:{changeTab(a){if(a.active){window.open(a.more,"_blank");return}const e=this.activeTab;e.active=false;a.active=true;this.content=a.name==="video"?"HomeVideo":"RankList"}}})}}})();

1
min/rank-list.vue.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>{return(t,r)=>{const e=`<div class=rank-list>wryyyy</div>`;return{export:Object.assign({template:e},{})}}})();

1
min/video-card.vue.min.js vendored Normal file
View File

@ -0,0 +1 @@
(()=>{return(e,i)=>{const t=`<a class=video-card target=_blank :href="'https://www.bilibili.com/av' + aid"><div class=cover-container><dpi-img class=cover :src=coverUrl :size="{height: 120, width: 200}"></dpi-img><div class=duration>{{durationText}}</div><div class=watchlater @click.stop.prevent=toggleWatchlater()><i class=mdi :class="{'mdi-clock-outline': !watchlater, 'mdi-check-circle': watchlater}"></i>{{watchlater ? '已添加' : '稍后再看'}}</div></div><h1 class=title :title=title>{{title}}</h1><div class=topics v-if="topics && topics.length"><a class=topic v-for="topic of topics":key=topic.id target=_blank :href="'https://t.bilibili.com/topic/name/' + topic.name + '/feed'">#{{topic.name}}#</a></div><pre class="description single-line":title=description v-else>{{description}}</pre><a class=up :class="{'no-face': !upFaceUrl}"target=_blank :href="'https://space.bilibili.com/' + upID"><dpi-img v-if=upFaceUrl class=face :src=upFaceUrl :size=24></dpi-img><icon v-else icon=up type=extended></icon><div class=name :title=upName>{{upName}}</div></a><div class=stats><template v-if=like><icon type=extended icon=like></icon>{{like}}</template><icon type=extended icon=play></icon>{{playCount}}<template v-if=danmakuCount><icon type=extended icon=danmaku></icon>{{danmakuCount}}</template></div></a>`;i.applyStyleFromText(`.video-card{display:grid;grid-template-columns:200px 1fr;grid-template-rows:3fr 2fr 3fr;grid-template-areas:"cover title" "cover description" "cover up";height:var(--card-height);width:var(--card-width);color:#000;border-radius:16px;box-shadow:0 2px 6px 0 #0002;margin-right:var(--card-margin);margin-bottom:var(--card-margin)}body.dark .video-card{background-color:#282828;color:#fff}.video-card>*{justify-self:self-start;align-self:center}.video-card:hover{transform:scale(1.02);transition:.1s cubic-bezier(.39,.58,.57,1)}.video-card:hover .duration,.video-card:hover .watchlater{opacity:1}.video-card .duration,.video-card .watchlater{opacity:0}.video-card .cover-container{grid-area:cover;position:relative}.video-card .cover-container .cover{border-radius:16px 0 0 16px;object-fit:cover}.video-card .cover-container>:not(.cover){position:absolute}.video-card .cover-container .duration,.video-card .cover-container .watchlater{bottom:6px;padding:4px 8px;background-color:#000a;color:#fff;border-radius:14px;height:24px}.video-card .cover-container .duration .mdi,.video-card .cover-container .watchlater .mdi{font-size:12pt;line-height:1;margin-right:4px}.video-card .cover-container .duration{left:6px}.video-card .cover-container .watchlater{right:6px;display:flex;align-items:center;padding-left:4px}.video-card .title{grid-area:title;font-size:12pt;font-weight:700;color:inherit;padding:0 12px;white-space:nowrap;overflow:hidden;justify-self:stretch;text-overflow:ellipsis}.video-card .topics{grid-area:description;display:flex;align-items:center;margin-left:12px}.video-card .topics .topic{color:inherit;padding:4px 8px;background-color:#8882;margin-right:8px;border-radius:14px}.video-card .description{grid-area:description;color:inherit;overflow:auto;align-self:stretch;justify-self:stretch;margin:0 12px;white-space:pre-wrap;line-height:1.5;scrollbar-width:none!important}.video-card .description::-webkit-scrollbar{width:0!important}.video-card .description.single-line{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.video-card .stats,.video-card .up{grid-area:up}.video-card .up{margin-left:12px;display:flex;align-items:center;padding:2px;background-color:#8882;border-radius:14px;color:inherit}.video-card .up .face{border-radius:50%}.video-card .up .name{margin:0 8px}.video-card .up:hover .name{color:var(--theme-color)}.video-card .stats{justify-self:self-end;margin-right:12px;display:flex;align-items:center;opacity:.5}.video-card .stats .be-icon{font-size:12pt;margin:0 4px 0 12px}`,"video-card-style");return{export:Object.assign({template:t},{props:["data"],components:{"dpi-img":()=>i.importAsync("dpi-img.vue"),Icon:()=>i.importAsync("icon.vue")},data(){return{...this.data}},methods:{async toggleWatchlater(){try{this.watchlater=!this.watchlater;const{toggleWatchlater:e}=await i.importAsync("watchlater-api");e(this.aid.toString(),this.watchlater)}catch(e){this.watchlater=!this.watchlater;Toast.error(e.message,"稍后再看操作失败",3e3)}}}})}}})();

View File

@ -4,103 +4,24 @@
<i class="mdi mdi-18px mdi-loading mdi-spin"></i>加载中...
</div>
<div class="cards" v-else-if="cards.length">
<a
class="home-video-card"
v-for="card in cards"
:key="card.id"
target="_blank"
:href="'https://www.bilibili.com/av' + card.aid"
>
<div class="cover-container">
<dpi-img class="cover" :src="card.coverUrl" :size="{height: 120, width: 200}"></dpi-img>
<div class="duration">{{card.durationText}}</div>
<div class="watchlater" @click.stop.prevent="watchlater(card.id)">
<i
class="mdi"
:class="{'mdi-clock-outline': !card.watchlater, 'mdi-check-circle': card.watchlater}"
></i>
{{card.watchlater ? '已添加' : '稍后再看'}}
</div>
</div>
<h1 class="title" :title="card.title">{{card.title}}</h1>
<div class="topics" v-if="card.topics.length">
<a
class="topic"
v-for="topic of card.topics"
:key="topic.id"
target="_blank"
:href="'https://t.bilibili.com/topic/name/' + topic.name + '/feed'"
>#{{topic.name}}#</a>
</div>
<pre class="description single-line" :title="card.description" v-else>{{card.description}}</pre>
<a class="up" target="_blank" :href="'https://space.bilibili.com/' + card.upID">
<dpi-img class="face" :src="card.upFaceUrl" :size="24"></dpi-img>
<div class="name" :title="card.upName">{{card.upName}}</div>
</a>
<div class="stats">
<Icon type="extended" icon="like"></Icon>
{{card.like}}
<Icon type="extended" icon="play"></Icon>
{{card.playCount}}
<Icon type="extended" icon="danmaku"></Icon>
{{card.danmakuCount}}
</div>
</a>
<video-card v-for="card of cards" :key="card.id" :data="card"></video-card>
</div>
<div class="empty" v-else>空空如也哦 =ω=</div>
</div>
</template>
<script lang="ts">
interface VideoCard {
id: string
aid: number
title: string
upID: number
upName: string
upFaceUrl: string
coverUrl: string
description: string
duration: number
durationText: string
playCount: string
danmakuCount: string
dynamic: string
like: string
timestamp: number
time: Date
topics: {
id: number
name: string
}[]
watchlater: boolean
}
import { VideoCardInfo } from '../video-card.vue'
export default {
components: {
'dpi-img': () => import('../../dpi-img.vue'),
Icon: () => import('../../icon.vue')
VideoCard: () => import('../video-card.vue'),
},
data() {
return {
cards: [] as VideoCard[],
cards: [] as VideoCardInfo[],
loading: true
}
},
methods: {
async watchlater(id: string) {
const card = this.cards.find((it: VideoCard) => it.id === id) as VideoCard
try {
card.watchlater = !card.watchlater
const { toggleWatchlater } = await import(
'../../../video/watchlater-api'
)
toggleWatchlater(card.aid.toString(), card.watchlater)
} catch (error) {
card.watchlater = !card.watchlater
Toast.error(error.message, '稍后再看操作失败', 3000)
}
}
},
async mounted() {
try {
const json = await Ajax.getJsonWithCredentials(
@ -114,7 +35,7 @@ export default {
throw new Error(json.message)
}
this.cards = json.data.cards.map(
(c: any): VideoCard => {
(c: any): VideoCardInfo => {
const card = JSON.parse(c.card)
const topics = _.get(c, 'display.topic_info.topic_details', []).map(
(it: any) => {
@ -160,157 +81,6 @@ export default {
display: flex;
flex-wrap: wrap;
align-items: center;
.home-video-card {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 3fr 2fr 3fr;
grid-template-areas:
'cover title'
'cover description'
'cover up';
height: var(--card-height);
width: var(--card-width);
color: black;
border-radius: 16px;
box-shadow: 0 2px 6px 0 #0002;
margin-right: var(--card-margin);
margin-bottom: var(--card-margin);
body.dark & {
background-color: #282828;
color: white;
}
& > * {
justify-self: self-start;
align-self: center;
}
&:hover {
transform: scale(1.02);
transition: 0.1s cubic-bezier(0.39, 0.58, 0.57, 1);
.duration,
.watchlater {
opacity: 1;
}
}
.duration,
.watchlater {
opacity: 0;
}
.cover-container {
grid-area: cover;
position: relative;
.cover {
border-radius: 16px 0 0 16px;
object-fit: cover;
}
& > :not(.cover) {
position: absolute;
}
.duration,
.watchlater {
bottom: 6px;
padding: 4px 8px;
background-color: #000a;
color: white;
border-radius: 14px;
height: 24px;
.mdi {
font-size: 12pt;
line-height: 1;
margin-right: 4px;
}
}
.duration {
left: 6px;
}
.watchlater {
right: 6px;
display: flex;
align-items: center;
padding-left: 4px;
}
}
.title {
grid-area: title;
font-size: 12pt;
font-weight: bold;
color: inherit;
padding: 0 12px;
white-space: nowrap;
overflow: hidden;
justify-self: stretch;
text-overflow: ellipsis;
}
.topics {
grid-area: description;
display: flex;
align-items: center;
margin-left: 12px;
.topic {
color: inherit;
padding: 4px 8px;
background-color: #8882;
margin-right: 8px;
border-radius: 14px;
}
}
.description {
grid-area: description;
color: inherit;
overflow: auto;
align-self: stretch;
justify-self: stretch;
margin: 0 12px;
white-space: pre-wrap;
line-height: 1.5;
scrollbar-width: none !important;
&::-webkit-scrollbar {
width: 0px !important;
}
&.single-line {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.up,
.stats {
grid-area: up;
}
.up {
margin-left: 12px;
display: flex;
align-items: center;
padding: 2px;
background-color: #8882;
border-radius: 14px;
color: inherit;
.face {
border-radius: 50%;
}
.name {
margin: 0 8px;
}
&:hover .name {
color: var(--theme-color);
}
}
.stats {
justify-self: self-end;
margin-right: 12px;
display: flex;
align-items: center;
opacity: 0.5;
.be-icon {
font-size: 12pt;
margin: 0 4px 0 12px;
}
}
}
}
}
</style>

View File

@ -15,7 +15,7 @@
<!-- <Search></Search> -->
</div>
<div class="minimal-home-content">
<component :is="content"></component>
<component :is="content" :key="activeTab.name" :rank-days="rankDays"></component>
</div>
</div>
</template>
@ -27,53 +27,68 @@ interface Tab {
displayName: string
active: boolean
more: string
rankDays?: number
}
const tabs = [
{
name: 'video',
displayName: '视频动态',
active: true,
more: 'https://t.bilibili.com/?tab=8'
},
{
name: 'ranking7',
displayName: '一周排行',
active: false,
more: 'https://www.bilibili.com/ranking/all/0/0/7',
rankDays: 7
},
{
name: 'ranking3',
displayName: '三日排行',
active: false,
more: 'https://www.bilibili.com/ranking',
rankDays: 3
},
{
name: 'ranking1',
displayName: '昨日排行',
active: false,
more: 'https://www.bilibili.com/ranking/all/0/0/1',
rankDays: 1
}
] as Tab[]
export default {
components: {
Icon: () => import('../../icon.vue'),
// Search: () => import('../../search.vue'),
HomeVideo: () => import('./home-video.vue')
HomeVideo: () => import('./home-video.vue'),
RankList: () => import('./rank-list.vue')
},
data() {
return {
tabs: [
{
name: 'video',
displayName: '视频动态',
active: true,
more: 'https://t.bilibili.com/?tab=8'
},
{
name: 'ranking7',
displayName: '一周排行',
active: false,
more: 'https://www.bilibili.com/ranking/all/0/0/7'
},
{
name: 'ranking3',
displayName: '三日排行',
active: false,
more: 'https://www.bilibili.com/ranking'
},
{
name: 'ranking1',
displayName: '昨日排行',
active: false,
more: 'https://www.bilibili.com/ranking/all/0/0/1'
}
] as Tab[],
tabs,
content: 'HomeVideo'
}
},
computed: {
activeTab() {
return this.tabs.find((t: Tab) => t.active) as Tab
},
rankDays(): number {
return this.activeTab.rankDays || 0
}
},
methods: {
changeTab(tab: Tab) {
if (tab.active) {
window.open(tab.more, '_blank')
return
}
const activeTab = this.tabs.find((t: Tab) => t.active) as Tab
const activeTab = this.activeTab
activeTab.active = false
tab.active = true
this.content = tab.name === 'video' ? 'HomeVideo' : 'RankList'
}
}
}

View File

@ -0,0 +1,18 @@
<template>
<div class="rank-list">
<!-- <div class="loading" v-if="loading">
<i class="mdi mdi-18px mdi-loading mdi-spin"></i>加载中...
</div>
<div class="cards" v-else-if="cards.length">
</div>
<div class="empty" v-else>空空如也哦 =ω=</div> -->
wryyyy
</div>
</template>
<script lang="ts">
export default {
}
</script>

View File

@ -0,0 +1,249 @@
<template>
<a class="video-card" target="_blank" :href="'https://www.bilibili.com/av' + aid">
<div class="cover-container">
<dpi-img class="cover" :src="coverUrl" :size="{height: 120, width: 200}"></dpi-img>
<div class="duration">{{durationText}}</div>
<div class="watchlater" @click.stop.prevent="toggleWatchlater()">
<i class="mdi" :class="{'mdi-clock-outline': !watchlater, 'mdi-check-circle': watchlater}"></i>
{{watchlater ? '已添加' : '稍后再看'}}
</div>
</div>
<h1 class="title" :title="title">{{title}}</h1>
<div class="topics" v-if="topics && topics.length">
<a
class="topic"
v-for="topic of topics"
:key="topic.id"
target="_blank"
:href="'https://t.bilibili.com/topic/name/' + topic.name + '/feed'"
>#{{topic.name}}#</a>
</div>
<pre class="description single-line" :title="description" v-else>{{description}}</pre>
<a
class="up"
:class="{'no-face': !upFaceUrl}"
target="_blank"
:href="'https://space.bilibili.com/' + upID"
>
<dpi-img v-if="upFaceUrl" class="face" :src="upFaceUrl" :size="24"></dpi-img>
<Icon v-else icon="up" type="extended"></Icon>
<div class="name" :title="upName">{{upName}}</div>
</a>
<div class="stats">
<template v-if="like">
<Icon type="extended" icon="like"></Icon>
{{like}}
</template>
<Icon type="extended" icon="play"></Icon>
{{playCount}}
<template v-if="danmakuCount">
<Icon type="extended" icon="danmaku"></Icon>
{{danmakuCount}}
</template>
</div>
</a>
</template>
<script lang="ts">
export interface VideoCardInfo {
id: string
aid: number
title: string
upID: number
upName: string
upFaceUrl?: string
coverUrl: string
description: string
duration?: number
durationText: string
playCount: string
danmakuCount?: string
dynamic?: string
like?: string
coins?: string
timestamp?: number
time?: Date
topics?: {
id: number
name: string
}[]
watchlater: boolean
}
export default {
props: ['data'],
components: {
'dpi-img': () => import('../dpi-img.vue'),
Icon: () => import('../icon.vue')
},
data() {
return {
...this.data
} as VideoCardInfo
},
methods: {
async toggleWatchlater() {
try {
this.watchlater = !this.watchlater
const { toggleWatchlater: toggle } = await import('../../video/watchlater-api')
toggle(this.aid.toString(), this.watchlater)
} catch (error) {
this.watchlater = !this.watchlater
Toast.error(error.message, '稍后再看操作失败', 3000)
}
}
}
}
</script>
<style lang="scss">
.video-card {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 3fr 2fr 3fr;
grid-template-areas:
'cover title'
'cover description'
'cover up';
height: var(--card-height);
width: var(--card-width);
color: black;
border-radius: 16px;
box-shadow: 0 2px 6px 0 #0002;
margin-right: var(--card-margin);
margin-bottom: var(--card-margin);
body.dark & {
background-color: #282828;
color: white;
}
& > * {
justify-self: self-start;
align-self: center;
}
&:hover {
transform: scale(1.02);
transition: 0.1s cubic-bezier(0.39, 0.58, 0.57, 1);
.duration,
.watchlater {
opacity: 1;
}
}
.duration,
.watchlater {
opacity: 0;
}
.cover-container {
grid-area: cover;
position: relative;
.cover {
border-radius: 16px 0 0 16px;
object-fit: cover;
}
& > :not(.cover) {
position: absolute;
}
.duration,
.watchlater {
bottom: 6px;
padding: 4px 8px;
background-color: #000a;
color: white;
border-radius: 14px;
height: 24px;
.mdi {
font-size: 12pt;
line-height: 1;
margin-right: 4px;
}
}
.duration {
left: 6px;
}
.watchlater {
right: 6px;
display: flex;
align-items: center;
padding-left: 4px;
}
}
.title {
grid-area: title;
font-size: 12pt;
font-weight: bold;
color: inherit;
padding: 0 12px;
white-space: nowrap;
overflow: hidden;
justify-self: stretch;
text-overflow: ellipsis;
}
.topics {
grid-area: description;
display: flex;
align-items: center;
margin-left: 12px;
.topic {
color: inherit;
padding: 4px 8px;
background-color: #8882;
margin-right: 8px;
border-radius: 14px;
}
}
.description {
grid-area: description;
color: inherit;
overflow: auto;
align-self: stretch;
justify-self: stretch;
margin: 0 12px;
white-space: pre-wrap;
line-height: 1.5;
scrollbar-width: none !important;
&::-webkit-scrollbar {
width: 0px !important;
}
&.single-line {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.up,
.stats {
grid-area: up;
}
.up {
margin-left: 12px;
display: flex;
align-items: center;
padding: 2px;
background-color: #8882;
border-radius: 14px;
color: inherit;
.face {
border-radius: 50%;
}
.name {
margin: 0 8px;
}
&:hover .name {
color: var(--theme-color);
}
}
.stats {
justify-self: self-end;
margin-right: 12px;
display: flex;
align-items: center;
opacity: 0.5;
.be-icon {
font-size: 12pt;
margin: 0 4px 0 12px;
}
}
}
</style>