Add infinite scroll

This commit is contained in:
the1812 2020-01-31 11:31:03 +08:00
parent 02e2b4539e
commit fabfcf9351
8 changed files with 71 additions and 37 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

@ -37,7 +37,7 @@
"custom-navbar-blank.min.js": "DC2B75D5760EECD3A257785FDFBB7E5DEBF61CD9083C68DD5BCE468B093F54F3",
"custom-navbar-category.min.js": "4CF38ACE1D0501217266D1E35DFCBF9D5C1D9A3838C54D53C614878BB8CF55E2",
"custom-navbar-component.min.js": "D1B98EDF5C32D19E4A009D2303F69207B35ED35DD06E0F161E7D0BBB313F3C37",
"custom-navbar-favorites-list.min.js": "8F4387F2E99B6D25DF5325EAFB158E159217FAFC586EAA3D2C055E862DB459A7",
"custom-navbar-favorites-list.min.js": "86B4B02CAC59E5715D30B8B74A29AA9A2CCD7EB47DC2EA00686B5E569B49886B",
"custom-navbar-iframe.min.js": "C4EE469EC09655F9FF535ACBBD2A5F7C332DA2081936F8F485F5FB015EBD794B",
"custom-navbar-logo.min.js": "236CCC887A3917A6D4840889B7380B648197765B8CDA27CB233F3F1E3215F7BB",
"custom-navbar-messages.min.js": "D7B315A4D8FD9E2EF9C1E40C1F3CA7EC18C5A87AC7A9410049061A0D851868DE",

Binary file not shown.

View File

@ -1 +1 @@
(()=>{return(t,i)=>{const{NavbarComponent:a}=i.import("custom-navbar-component");class e extends a{constructor(){super();this.noPadding=true;this.href=`https://space.bilibili.com/${getUID()}/favlist`;this.html=`收藏`;this.active=document.URL.replace(/\?.*$/,"")===`https://space.bilibili.com/${getUID()}/favlist`;this.popupHtml=`\n<div class="favorites-list loading">\n<div class="loading-tip">\n 加载中...\n</div>\n<div class="content">\n<div class="header">\n<v-dropdown class="list-select" @change="changeList()" :round="true" :items="listNames" :value.sync="selectedListName">\n</v-dropdown>\n<a class="more-info" :href="moreLink" title="查看更多" target="_blank">\n 查看更多\n<i class="mdi mdi-dots-horizontal"></i>\n</a>\n</div>\n<transition-group name="cards" tag="div" class="cards">\n<div class="loading-tip" v-if="cardsLoading" key="loading-tip">\n 加载中...\n</div>\n<div class="empty-tip" v-if="!cardsLoading && cards.length === 0" key="empty-tip">\n 空空如也哦 = ̄ω ̄=\n</div>\n<div class="favorite-card" v-for="card of cards" :key="card.id">\n<a class="cover-container" target="_blank" :href="'https://www.bilibili.com/video/av' + card.aid">\n<dpi-img class="cover" :src="card.coverUrl" :size="{width: 130, height: 85}"></dpi-img>\n<div class="floating duration">{{card.durationText}}</div>\n<div class="floating favorite-time">{{card.favoriteTime | formatDate}}</div>\n</a>\n<a class="title" target="_blank" :href="'https://www.bilibili.com/video/av' + card.aid" :title="card.title">{{card.title}}</a>\n<a class="up" target="_blank" :href="'https://space.bilibili.com/' + card.upID" :title="card.upName">\n<dpi-img class="face" :src="card.upFaceUrl" :size="20"></dpi-img>\n<div class="name">{{card.upName}}</div>\n</a>\n</div>\n</transition-group>\n</div>\n</div>\n`;this.initialPopup=(()=>this.init())}get name(){return"favoritesList"}async init(){new Vue({el:await SpinQuery.select(`.custom-navbar [data-name="${this.name}"] .favorites-list`),store:store,filters:{formatDate(t){return`${t.getFullYear()}-${(t.getMonth()+1).toString().padEnd(2,"0")}-${t.getDate().toString().padEnd(2,"0")}`}},components:{DpiImg:()=>i.importAsync("dpi-img.vue"),VDropdown:()=>i.importAsync("v-dropdown.vue")},data:{list:[],cards:[],selectedListName:"",cardsLoading:true},computed:{listNames(){return this.list.map(t=>t.name)},selectedListId(){const t=this.list.find(t=>t.name===this.selectedListName);return t?t.id:0},moreLink(){const t=this.selectedListId;if(t===0){return`https://space.bilibili.com/${getUID()}/favlist`}return`https://space.bilibili.com/${getUID()}/favlist?fid=${t}`}},methods:{async changeList(){try{this.cards=[];this.cardsLoading=true;const t=`https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id=${this.selectedListId}&pn=1&ps=20`;const i=await Ajax.getJsonWithCredentials(t);if(i.code!==0){throw new Error(`加载收藏夹内容失败: ${i.message}`)}this.cards=i.data.medias.filter(t=>{return t.attr===0}).map(t=>{return{id:t.id,aid:t.id,coverUrl:t.cover.replace("http:","https:"),favoriteTimestamp:t.fav_time*1e3,favoriteTime:new Date(t.fav_time*1e3),title:t.title,description:t.intro,duration:t.duration,durationText:formatDuration(t.duration),playCount:t.cnt_info.play,danmakuCount:t.cnt_info.danmaku,upName:t.upper.name,upFaceUrl:t.upper.face.replace("http:","https:"),upID:t.upper.mid}})}catch(t){logError(t)}finally{this.cardsLoading=false}}},async mounted(){try{const t=`https://api.bilibili.com/medialist/gateway/base/created?pn=1&ps=100&up_mid=${getUID()}&is_space=0`;const i=await Ajax.getJsonWithCredentials(t);if(i.code!==0){throw new Error(`获取收藏夹列表失败: ${i.message}`)}this.list=i.data.list.map(t=>{return{id:t.id,name:t.title,count:t.media_count}});if(this.list.length>0){this.selectedListName=this.list[0].name;this.changeList()}}catch(t){logError(t)}finally{this.$el.classList.remove("loading")}}})}}return{export:{FavoritesList:e}}}})();
(()=>{return(t,i)=>{const{NavbarComponent:e}=i.import("custom-navbar-component");class a extends e{constructor(){super();this.noPadding=true;this.href=`https://space.bilibili.com/${getUID()}/favlist`;this.html=`收藏`;this.active=document.URL.replace(/\?.*$/,"")===`https://space.bilibili.com/${getUID()}/favlist`;this.popupHtml=`\n<div class="favorites-list loading">\n<div class="loading-tip">\n 加载中...\n</div>\n<div class="content">\n<div class="header">\n<v-dropdown class="list-select" @change="changeList()" :round="true" :items="listNames" :value.sync="selectedListName">\n</v-dropdown>\n<a class="more-info" :href="moreLink" title="查看更多" target="_blank">\n 查看更多\n<i class="mdi mdi-dots-horizontal"></i>\n</a>\n</div>\n<transition-group name="cards" tag="div" class="cards">\n<div class="empty-tip" v-if="!cardsLoading && cards.length === 0" key="empty-tip">\n 空空如也哦 = ̄ω ̄=\n</div>\n<div class="favorite-card" v-for="card of cards" :key="card.id">\n<a class="cover-container" target="_blank" :href="'https://www.bilibili.com/video/av' + card.aid">\n<dpi-img class="cover" :src="card.coverUrl" :size="{width: 130, height: 85}"></dpi-img>\n<div class="floating duration">{{card.durationText}}</div>\n<div class="floating favorite-time">{{card.favoriteTime | formatDate}}</div>\n</a>\n<a class="title" target="_blank" :href="'https://www.bilibili.com/video/av' + card.aid" :title="card.title">{{card.title}}</a>\n<a class="up" target="_blank" :href="'https://space.bilibili.com/' + card.upID" :title="card.upName">\n<dpi-img class="face" :src="card.upFaceUrl" :size="20"></dpi-img>\n<div class="name">{{card.upName}}</div>\n</a>\n</div>\n<div class="loading-tip" v-if="cardsLoading" key="loading-tip">\n 加载中...\n</div>\n</transition-group>\n</div>\n</div>\n`;this.initialPopup=(()=>this.init())}get name(){return"favoritesList"}async init(){new Vue({el:await SpinQuery.select(`.custom-navbar [data-name="${this.name}"] .favorites-list`),store:store,filters:{formatDate(t){return`${t.getFullYear()}-${(t.getMonth()+1).toString().padEnd(2,"0")}-${t.getDate().toString().padEnd(2,"0")}`}},components:{DpiImg:()=>i.importAsync("dpi-img.vue"),VDropdown:()=>i.importAsync("v-dropdown.vue")},data:{list:[],cards:[],selectedListName:"",cardsLoading:true,cardsPage:1},computed:{listNames(){return this.list.map(t=>t.name)},selectedListId(){const t=this.list.find(t=>t.name===this.selectedListName);return t?t.id:0},moreLink(){const t=this.selectedListId;if(t===0){return`https://space.bilibili.com/${getUID()}/favlist`}return`https://space.bilibili.com/${getUID()}/favlist?fid=${t}`}},methods:{async getCards(){const t=`https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id=${this.selectedListId}&pn=${this.cardsPage}&ps=20`;const i=await Ajax.getJsonWithCredentials(t);if(i.code!==0){throw new Error(`加载收藏夹内容失败: ${i.message}`)}if(!i.data.medias){return[]}return i.data.medias.filter(t=>{return t.attr===0}).map(t=>{return{id:t.id,aid:t.id,coverUrl:t.cover.replace("http:","https:"),favoriteTimestamp:t.fav_time*1e3,favoriteTime:new Date(t.fav_time*1e3),title:t.title,description:t.intro,duration:t.duration,durationText:formatDuration(t.duration),playCount:t.cnt_info.play,danmakuCount:t.cnt_info.danmaku,upName:t.upper.name,upFaceUrl:t.upper.face.replace("http:","https:"),upID:t.upper.mid}})},async changeList(){try{this.cards=[];this.cardsPage=1;this.cardsLoading=true;this.cards=await this.getCards();this.setInfiniteScroll()}catch(t){logError(t)}finally{this.cardsLoading=false}},async loadNextPage(){try{this.cardsLoading=true;this.cardsPage++;const t=await this.getCards();this.cards.push(...t);if(t.length>0){this.setInfiniteScroll()}}catch(t){logError(t)}finally{this.cardsLoading=false}},setInfiniteScroll(){const t=this.$el.querySelector(".cards");const i=_.debounce(()=>{if(t.scrollTop+t.clientHeight>=t.scrollHeight-48){t.removeEventListener("scroll",i);this.loadNextPage()}},200);t.addEventListener("scroll",i)}},async mounted(){try{const t=`https://api.bilibili.com/medialist/gateway/base/created?pn=1&ps=100&up_mid=${getUID()}&is_space=0`;const i=await Ajax.getJsonWithCredentials(t);if(i.code!==0){throw new Error(`获取收藏夹列表失败: ${i.message}`)}this.list=i.data.list.map(t=>{return{id:t.id,name:t.title,count:t.media_count}});if(this.list.length>0){this.selectedListName=this.list[0].name;this.changeList()}}catch(t){logError(t)}finally{this.$el.classList.remove("loading")}}})}}return{export:{FavoritesList:a}}}})();

View File

@ -31,9 +31,6 @@ export class FavoritesList extends NavbarComponent {
</a>
</div>
<transition-group name="cards" tag="div" class="cards">
<div class="loading-tip" v-if="cardsLoading" key="loading-tip">
...
</div>
<div class="empty-tip" v-if="!cardsLoading && cards.length === 0" key="empty-tip">
=ω=
</div>
@ -49,6 +46,9 @@ export class FavoritesList extends NavbarComponent {
<div class="name">{{card.upName}}</div>
</a>
</div>
<div class="loading-tip" v-if="cardsLoading" key="loading-tip">
...
</div>
</transition-group>
</div>
</div>
@ -76,6 +76,7 @@ export class FavoritesList extends NavbarComponent {
cards: [],
selectedListName: '',
cardsLoading: true,
cardsPage: 1,
},
computed: {
listNames() {
@ -94,41 +95,74 @@ export class FavoritesList extends NavbarComponent {
},
},
methods: {
async getCards() {
const url = `https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id=${this.selectedListId}&pn=${this.cardsPage}&ps=20`
const json = await Ajax.getJsonWithCredentials(url)
if (json.code !== 0) {
throw new Error(`加载收藏夹内容失败: ${json.message}`)
}
if (!json.data.medias) { // 超过最后一页后返回空数组
return []
}
return json.data.medias.filter((item: any) => {
return item.attr === 0 // 过滤掉已失效视频
}).map((item: any) => {
return {
id: item.id,
aid: item.id,
coverUrl: item.cover.replace('http:', 'https:'),
favoriteTimestamp: item.fav_time * 1000,
favoriteTime: new Date(item.fav_time * 1000),
title: item.title,
description: item.intro,
duration: item.duration,
durationText: formatDuration(item.duration),
playCount: item.cnt_info.play,
danmakuCount: item.cnt_info.danmaku,
upName: item.upper.name,
upFaceUrl: item.upper.face.replace('http:', 'https:'),
upID: item.upper.mid,
} as FavoritesItemInfo
})
},
async changeList() {
try {
this.cards = []
this.cardsPage = 1
this.cardsLoading = true
const url = `https://api.bilibili.com/medialist/gateway/base/spaceDetail?media_id=${this.selectedListId}&pn=1&ps=20`
const json = await Ajax.getJsonWithCredentials(url)
if (json.code !== 0) {
throw new Error(`加载收藏夹内容失败: ${json.message}`)
}
this.cards = json.data.medias.filter((item: any) => {
return item.attr === 0 // 过滤掉已失效视频
}).map((item: any) => {
return {
id: item.id,
aid: item.id,
coverUrl: item.cover.replace('http:', 'https:'),
favoriteTimestamp: item.fav_time * 1000,
favoriteTime: new Date(item.fav_time * 1000),
title: item.title,
description: item.intro,
duration: item.duration,
durationText: formatDuration(item.duration),
playCount: item.cnt_info.play,
danmakuCount: item.cnt_info.danmaku,
upName: item.upper.name,
upFaceUrl: item.upper.face.replace('http:', 'https:'),
upID: item.upper.mid,
} as FavoritesItemInfo
})
this.cards = await this.getCards()
this.setInfiniteScroll()
} catch (error) {
logError(error)
} finally {
this.cardsLoading = false
}
}
},
async loadNextPage() {
try {
this.cardsLoading = true
this.cardsPage++
const cards = await this.getCards()
this.cards.push(...cards)
if (cards.length > 0) {
this.setInfiniteScroll()
}
} catch (error) {
logError(error)
} finally {
this.cardsLoading = false
}
},
setInfiniteScroll() {
const cardsContainer = this.$el.querySelector('.cards') as HTMLElement
const scrollHandler = _.debounce(() => {
if (cardsContainer.scrollTop + cardsContainer.clientHeight >= cardsContainer.scrollHeight - 48) {
cardsContainer.removeEventListener('scroll', scrollHandler)
this.loadNextPage()
}
}, 200)
cardsContainer.addEventListener('scroll', scrollHandler)
},
},
async mounted() {
try {