Add bangumi ranklist

This commit is contained in:
the1812 2022-03-05 17:41:11 +08:00
parent 583f44dc59
commit 7d9bf609b3
9 changed files with 346 additions and 69 deletions

View File

@ -109,6 +109,8 @@ export default Vue.extend({
.fresh-home-categories {
@include v-stretch();
--fresh-home-categories-column-gap: 28px;
--fresh-home-categories-header-gap: 12px;
&-content {
font-size: 14px;
}

View File

@ -1,5 +1,160 @@
<template>
<div class="">
bangumi content
<div class="fresh-home-categories-bangumi">
<div class="fresh-home-categories-bangumi-timeline">
<div class="fresh-home-categories-bangumi-timeline-header">
<SubHeader>
时间表
</SubHeader>
<VButton
v-if="viewLastWeek"
class="fresh-home-header-icon-button fresh-home-categories-bangumi-timeline-down"
round
@click="verticalScroll(false)"
>
<VIcon icon="mdi-arrow-down" :size="20" />
本周
</VButton>
<VButton
v-else
class="fresh-home-header-icon-button fresh-home-categories-bangumi-timeline-up"
round
@click="verticalScroll(true)"
>
<VIcon icon="mdi-arrow-up" :size="20" />
上周
</VButton>
</div>
<BangumiTimeline
:view-last-week="viewLastWeek"
:api="bangumiDataMap[route].api"
/>
</div>
<div class="fresh-home-categories-bangumi-rank-list">
<a
class="fresh-home-categories-bangumi-rank-list-header"
:href="rankingsLink"
target="_blank"
>
<SubHeader>
排行榜
</SubHeader>
</a>
<RankList
bangumi-mode
:parse-json="parseJson"
:api="rankingsApi"
/>
</div>
</div>
</template>
<script lang="ts">
import {
VButton,
VIcon,
} from '@/ui'
import SubHeader from '../../../SubHeader.vue'
import RankList from './RankList.vue'
import BangumiTimeline from './BangumiTimeline.vue'
import { RankListCard } from './rank-list-card'
const bangumiDataMap = {
anime: {
api: 'https://bangumi.bilibili.com/web_api/timeline_global',
seasonType: 1,
rankingName: 'bangumi',
},
guochuang: {
api: 'https://bangumi.bilibili.com/web_api/timeline_cn',
seasonType: 4,
// "" b ...
rankingName: 'guochan',
},
}
export default Vue.extend({
components: {
SubHeader,
VButton,
VIcon,
BangumiTimeline,
RankList,
},
props: {
region: {
type: Object,
required: true,
},
},
data() {
const { route } = this.region.category
return {
viewLastWeek: false,
bangumiDataMap,
route,
rankingsApi: `https://api.bilibili.com/pgc/season/rank/web/list?day=3&season_type=${bangumiDataMap[route].seasonType}`,
rankingsLink: `https://www.bilibili.com/v/popular/rank/${bangumiDataMap[route].rankingName}`,
}
},
methods: {
verticalScroll(goUp: boolean) {
this.viewLastWeek = goUp
},
parseJson(json: any) {
const items = (json.data?.list ?? []) as any[]
return items
.map(
(item): RankListCard => ({
id: item.season_id,
title: item.title,
playCount: item.stat.view,
points: item.pts,
upHref: item.url,
upName: item.new_ep?.index_show ?? item.title,
coverUrl: item.new_ep?.cover ?? item.ss_horizontal_cover,
videoHref: item.url,
}),
)
.slice(0, 10)
},
},
})
</script>
<style lang="scss">
@import "common";
@import "./effects";
.fresh-home-categories-bangumi {
@include h-stretch(var(--fresh-home-categories-column-gap));
&-timeline {
flex: 1;
@include v-stretch(var(--fresh-home-categories-header-gap));
&-down {
&:hover .be-icon {
@include bounce-y(2);
}
&:active .be-icon {
transform: scale(0.9);
}
}
&-up {
&:hover .be-icon {
@include bounce-y(-2);
}
&:active .be-icon {
transform: scale(0.9);
}
}
&-header {
@include h-center();
justify-content: space-between;
}
}
&-rank-list {
@include v-stretch(var(--fresh-home-categories-header-gap));
&-header {
// Timeline header , 20px, padding 8px, line-height
line-height: calc(20px + 8px);
}
}
}
</style>

View File

@ -0,0 +1,56 @@
<template>
<div class="fresh-home-categories-bangumi-timeline-content">
timeline
</div>
</template>
<script lang="ts">
import { requestMixin } from './mixin'
interface TimelineSeason {
cover: string
delay: number
ep_id: number
favorites: number
follow: number
is_published: number
pub_index: string
pub_time: string
pub_ts: number
season_id: number
season_status: number
square_cover: string
title: string
url: string
}
interface TimelineDay {
date: string
date_ts: number
day_of_week: number
is_today: number
seasons: TimelineSeason[]
}
export default Vue.extend({
mixins: [requestMixin],
props: {
viewLastWeek: {
type: Boolean,
default: false,
},
},
methods: {
parseJson(json: any) {
return json.result ?? []
},
},
})
</script>
<style lang="scss">
@import "common";
.fresh-home-categories-bangumi {
&-timeline-content {
}
}
</style>

View File

@ -1,7 +1,5 @@
<template>
<div
class="fresh-home-categories-default"
>
<div class="fresh-home-categories-default">
<div class="fresh-home-categories-default-video-column">
<div class="fresh-home-categories-default-video-column-item">
<SubHeader>
@ -22,19 +20,24 @@
排行榜
</SubHeader>
</a>
<RankList :api="rankingsApi" />
<RankList
:parse-json="parseJson"
:api="rankingsApi"
/>
</div>
</div>
</template>
<script lang="ts">
import { RankListCard } from './rank-list-card'
import RankList from './RankList.vue'
import VideoSlides from './VideoSlides.vue'
import SubHeader from '../../../SubHeader.vue'
/*
TODO: 有几个区表面上是普通视频, 但内容走的还是番剧那套, 所以 RankList 也要换 API
视频排行: `https://api.bilibili.com/x/web-interface/ranking/region?rid=${regionCode}&day=3&original=0`
视频排行: `https://api.bilibili.com/x/web-interface/ranking/region?day=3&original=0&rid=${regionCode}`
番剧排行: `https://api.bilibili.com/pgc/season/rank/web/list?day=3&season_type=${seasonType}`
- seasonType: 1 番剧 2 电影 3 纪录片 4 国创 5 电视剧
*/
export default Vue.extend({
@ -59,23 +62,42 @@ export default Vue.extend({
rankingsLink: `https://www.bilibili.com/v/popular/rank/${this.region.category.route}`,
}
},
methods: {
parseJson(json: any) {
const items = (lodash.get(json, 'data', []) || []) as any[]
return items
.map(
(item): RankListCard => ({
id: item.aid,
title: item.title,
playCount: item.play,
points: item.pts,
upHref: `https://space.bilibili.com/${item.mid}`,
upName: item.author,
coverUrl: item.pic,
videoHref: `https://www.bilibili.com/video/${item.bvid}`,
}),
)
.slice(0, 10)
},
},
})
</script>
<style lang="scss">
@import "common";
.fresh-home-categories-default {
@include h-stretch(28px);
@include h-stretch(var(--fresh-home-categories-column-gap));
&-video-column {
@include v-stretch(16px);
flex: 1;
&-item {
@include v-stretch(12px);
@include v-stretch(var(--fresh-home-categories-header-gap));
}
}
&-rank-list {
@include v-stretch(12px);
@include v-stretch(var(--fresh-home-categories-header-gap));
}
}
</style>

View File

@ -20,7 +20,7 @@
<a
class="fresh-home-rank-list-rank-item-title"
target="_blank"
:href="videoHref(firstItem)"
:href="firstItem.videoHref"
:title="firstItem.title"
>
{{ firstItem.title }}
@ -28,7 +28,7 @@
<a
class="fresh-home-rank-list-cover"
target="_blank"
:href="videoHref(firstItem)"
:href="firstItem.videoHref"
>
<DpiImage
:src="firstItem.coverUrl"
@ -36,9 +36,13 @@
/>
<UpInfo
:up-face-url="firstItem.upFaceUrl"
:up-id="firstItem.upID"
:href="firstItem.upHref"
:up-name="firstItem.upName"
/>
>
<template #fallback-icon>
<VIcon v-bind="upInfoProps" />
</template>
</UpInfo>
<div class="fresh-home-rank-list-stats">
<VIcon icon="mdi-fire" :size="16" />
{{ firstItem.points | formatCount }}
@ -50,7 +54,7 @@
<a
class="fresh-home-rank-list-rank-item"
target="_blank"
:href="videoHref(secondItem)"
:href="secondItem.videoHref"
>
<div
class="fresh-home-rank-list-rank-item-title"
@ -60,9 +64,13 @@
</div>
<UpInfo
:up-face-url="secondItem.upFaceUrl"
:up-id="secondItem.upID"
:href="secondItem.upHref"
:up-name="secondItem.upName"
/>
>
<template #fallback-icon>
<VIcon v-bind="upInfoProps" />
</template>
</UpInfo>
<div class="fresh-home-rank-list-stats">
<VIcon icon="mdi-fire" :size="16" />
{{ secondItem.points | formatCount }}
@ -73,7 +81,7 @@
<a
class="fresh-home-rank-list-cover"
target="_blank"
:href="videoHref(secondItem)"
:href="secondItem.videoHref"
>
<DpiImage
:src="secondItem.coverUrl"
@ -86,7 +94,7 @@
<a
class="fresh-home-rank-list-rank-item"
target="_blank"
:href="videoHref(thirdItem)"
:href="thirdItem.videoHref"
>
<div
class="fresh-home-rank-list-rank-item-title"
@ -96,9 +104,13 @@
</div>
<UpInfo
:up-face-url="thirdItem.upFaceUrl"
:up-id="thirdItem.upID"
:href="thirdItem.upHref"
:up-name="thirdItem.upName"
/>
>
<template #fallback-icon>
<VIcon v-bind="upInfoProps" />
</template>
</UpInfo>
<div class="fresh-home-rank-list-stats">
<VIcon icon="mdi-fire" :size="16" />
{{ secondItem.points | formatCount }}
@ -109,7 +121,7 @@
<a
class="fresh-home-rank-list-cover"
target="_blank"
:href="videoHref(thirdItem)"
:href="thirdItem.videoHref"
>
<DpiImage
:src="thirdItem.coverUrl"
@ -122,7 +134,6 @@
</div>
</template>
<script lang="ts">
import { VideoCard } from '@/components/feeds/video-card'
import UpInfo from '@/components/feeds/UpInfo.vue'
import { formatCount } from '@/core/utils/formatters'
import {
@ -162,6 +173,16 @@ export default Vue.extend({
thirdCoverWidth: 139,
}),
],
props: {
parseJson: {
type: Function,
required: true,
},
bangumiMode: {
type: Boolean,
default: false,
},
},
computed: {
firstItem() {
return this.items[0]
@ -172,6 +193,15 @@ export default Vue.extend({
thirdItem() {
return this.items[2]
},
upInfoProps() {
return {
size: 18,
icon: this.bangumiMode ? 'mdi-television-classic' : 'up-outline',
style: {
transform: this.bangumiMode ? 'translateY(-1px)' : 'none',
},
}
},
firstRow() {
return this.items.slice(3, 6)
},
@ -179,33 +209,6 @@ export default Vue.extend({
return this.items.slice(6, 10)
},
},
methods: {
parseJson(json: any) {
const items = (lodash.get(json, 'data', []) || []) as any[]
return items
.map(
(item): VideoCard => ({
id: item.aid,
aid: parseInt(item.aid),
bvid: item.bvid,
title: item.title,
playCount: item.play,
favorites: item.favorites,
upID: item.mid,
upName: item.author,
description: item.description,
coverUrl: item.pic,
coins: item.coins,
durationText: item.duration,
points: item.pts,
}),
)
.slice(0, 10)
},
videoHref(videoCard: VideoCard) {
return `https://www.bilibili.com/video/${videoCard.bvid}`
},
},
})
</script>
<style lang="scss">
@ -216,6 +219,7 @@ export default Vue.extend({
flex: 1;
width: 400px;
overflow: hidden;
min-height: var(--panel-height);
height: var(--panel-height);
padding: var(--padding);
margin: calc(0px - var(--padding));
@ -241,6 +245,7 @@ export default Vue.extend({
& &-rank-item {
@include card(12px);
@include v-stretch();
border-radius: var(--home-card-radius);
justify-content: space-between;
padding: 10px 0;
height: var(--rank-item-height);

View File

@ -222,6 +222,7 @@ export default Vue.extend({
</script>
<style lang="scss">
@import "common";
@import "./effects";
.fresh-home-video-slides {
@include card(12px);
@ -235,19 +236,6 @@ export default Vue.extend({
padding: var(--main-padding-y) var(--main-padding-x);
height: 266px;
@mixin bounce-x($offset-in-px) {
@keyframes bounce-x-#{$offset-in-px} {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(#{$offset-in-px}px);
}
}
animation: bounce-x-#{$offset-in-px} 0.4s ease-out;
}
.cover-placeholder-vertical {
height: var(--other-cover-height);
width: 0;

View File

@ -0,0 +1,24 @@
@mixin bounce-x($offset-in-px) {
@keyframes bounce-x-#{$offset-in-px} {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(#{$offset-in-px}px);
}
}
animation: bounce-x-#{$offset-in-px} 0.4s ease-out;
}
@mixin bounce-y($offset-in-px) {
@keyframes bounce-y-#{$offset-in-px} {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(#{$offset-in-px}px);
}
}
animation: bounce-y-#{$offset-in-px} 0.4s ease-out;
}

View File

@ -0,0 +1,11 @@
export interface RankListCard {
id: string
title: string
videoHref: string
coverUrl: string
upFaceUrl?: string
upHref: string
upName: string
points: number
playCount: number
}

View File

@ -2,7 +2,7 @@
<a
class="be-up-info"
:class="{ fallback: !upFaceUrl }"
:href="`https://space.bilibili.com/${upId}`"
:href="actualHref"
:title="upName"
target="_blank"
>
@ -13,10 +13,12 @@
:src="upFaceUrl"
/>
<div v-else class="be-up-info-cover-fallback">
<slot name="fallback-icon">
<VIcon
icon="up-outline"
:size="18"
/>
</slot>
</div>
<div class="be-up-info-name">
{{ upName }}
@ -32,9 +34,13 @@ export default Vue.extend({
VIcon,
},
props: {
href: {
type: String,
default: '',
},
upId: {
type: [String, Number],
required: true,
default: '',
},
upFaceUrl: {
type: String,
@ -45,6 +51,14 @@ export default Vue.extend({
required: true,
},
},
computed: {
actualHref() {
if (this.href) {
return this.href
}
return `https://space.bilibili.com/${this.upId}`
},
},
})
</script>
<style lang="scss">