Merge pull request #5217 from OharaRinneY/dev

添加根据关注分组筛选动态功能
This commit is contained in:
Grant Howard 2025-04-28 20:47:59 +08:00 committed by GitHub
commit 406b2f6318
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 224 additions and 13 deletions

View File

@ -0,0 +1,156 @@
<template>
<div class="group-filter-panel">
<div class="group-filter-header">
<h1>分组</h1>
<switch-box v-model="allChecked" />
</div>
<div v-for="(group, index) in groups" :key="index" class="group-item">
<CheckBox v-model="group.checked">
{{ group.name }}
</CheckBox>
</div>
</div>
</template>
<script lang="ts">
import { CheckBox, SwitchBox } from '@/ui'
import { bilibiliApi, getJsonWithCredentials, getPages } from '@/core/ajax'
import { FeedsCard, forEachFeedsCard } from '@/components/feeds/api'
import { getUID } from '@/core/utils'
interface Group {
name: string
checked: boolean
id: number
}
let cardsManager: typeof import('@/components/feeds/api').feedsCardsManager
export default Vue.extend({
components: {
SwitchBox,
CheckBox,
},
data() {
return {
groups: [],
followingMap: new Map<string, number[]>(), // username -> tagid[]
selectedGroupIds: [],
allChecked: true,
} as {
groups: Group[]
followingMap: Map<string, number[]>
selectedGroupIds: number[]
allChecked: boolean
}
},
watch: {
groups: {
handler(newGroups: Group[]) {
this.selectedGroupIds = newGroups.filter(group => group.checked).map(group => group.id)
cardsManager.cards.forEach(card => {
this.updateCard(lodash.clone(card))
})
},
deep: true,
immediate: true,
},
allChecked: {
handler(newChecked: boolean) {
for (const group of this.groups) {
group.checked = newChecked
}
},
},
},
async mounted() {
cardsManager = await forEachFeedsCard({
added: card => {
this.updateCard(lodash.clone(card))
},
})
// fetch groups
this.groups = await bilibiliApi<Array<any>>(
getJsonWithCredentials('https://api.bilibili.com/x/relation/tags'),
'分组信息获取失败',
).then(res =>
res.map(value => ({
name: value.name,
checked: true,
id: value.tagid,
})),
)
const uid = getUID()
// fetch following
const allPages = await getPages({
api: page =>
getJsonWithCredentials(
`https://api.bilibili.com/x/relation/followings?vmid=${uid}&pn=${page}&ps=50`,
),
getList: json => json.data.list,
getTotal: json => json.data.total,
})
allPages.forEach(user => {
this.followingMap.set(user.uname, user.tag)
})
},
methods: {
updateCard(card: FeedsCard) {
// usernametag
const userTagIds: number[] = this.followingMap.get(card.username)
if (!userTagIds || !this.selectedGroupIds) {
return
}
if (!userTagIds.some(item => this.selectedGroupIds.includes(item))) {
card.element.classList.add('group-filter-hide-feed')
} else {
card.element.classList.remove('group-filter-hide-feed')
}
},
},
})
</script>
<style lang="scss">
.group-filter-hide-feed {
display: none !important;
}
.group-filter-panel {
background-color: white;
font-size: 12px;
width: 100%;
border-radius: 4px;
box-sizing: border-box;
flex-direction: column;
padding: 12px 16px;
.group-filter-header {
cursor: pointer;
padding-bottom: 14px;
position: sticky;
top: 0;
display: flex;
align-items: center;
justify-content: space-between;
h1 {
font-weight: normal;
font-size: 16px;
margin: 0;
}
}
.group-item {
display: flex;
flex-direction: row;
font-size: 14px;
}
body.dark & {
color: #eee;
background-color: #444;
}
}
</style>

View File

@ -0,0 +1 @@
按照关注分组筛选动态

View File

@ -0,0 +1,34 @@
import { defineComponentMetadata } from '@/components/define'
import { feedsCardsManager } from '@/components/feeds/api'
const entry = async () => {
const { select } = await import('@/core/spin-query')
let leftPanel: HTMLElement
if (feedsCardsManager.managerType === 'v2') {
const leftAside = await select('.bili-dyn-home--member aside.left')
const section = document.createElement('section')
section.classList.add('group-filter-section')
leftAside.insertAdjacentElement('afterbegin', section)
leftPanel = section
} else {
leftPanel = await select('.home-container .left-panel')
}
if (leftPanel === null) {
return
}
const FilterPanel = await import('./FilterPanel.vue')
const { mountVueComponent } = await import('@/core/utils')
leftPanel.insertAdjacentElement('afterbegin', mountVueComponent(FilterPanel).$el)
}
export const component = defineComponentMetadata({
name: 'feedsGroupFilter',
entry,
displayName: '动态分组过滤',
author: { name: 'Rinne', link: 'https://github.com/OharaRinneY' },
urlInclude: [
// 仅动态首页
/^https:\/\/t\.bilibili\.com\/$/,
],
tags: [componentsTags.feeds],
})

View File

@ -194,27 +194,45 @@ export const responsiveGetPages = <T = any>(config: {
responsivePromise = new Promise(resolveResponsive => {
;(async () => {
const { api, getList, getTotal } = config
let page = 1
let total = Infinity
const result = []
while (result.length < total) {
const json = await api(page)
const result: T[] = []
const fetchPage = async (p: number): Promise<T[]> => {
const json = await api(p)
if (json.code !== 0) {
console.warn(
`api failed in ajax.getPages. message = ${json.message}, page = ${page}, total = ${total}, api = `,
`api failed in ajax.getPages. message = ${json.message}, page = ${p}, total = ${total}, api = `,
api,
)
return []
}
const list = getList(json)
result.push(...list)
if (page === 1) {
resolveResponsive(result)
}
page++
if (total === Infinity) {
total = getTotal(json)
return getList(json)
}
// 请求第一次获得total
const firstReq = await api(1)
result.push(...getList(firstReq))
total = getTotal(firstReq)
const pageSize = getList(firstReq).length || 1 // 防止为0
const totalPages = Math.ceil(total / pageSize)
resolveResponsive(result) // 第一页
if (totalPages === 1) {
resolveTotal(result)
return
}
// 收集Promise每5个为一个batch运行
const batchSize = 5
let batch: Promise<T[]>[] = []
for (let i = 2; i <= totalPages; i++) {
batch.push(fetchPage(i))
if (batch.length === batchSize || i === totalPages) {
const lists = await Promise.all(batch)
result.push(...lists.flat())
batch = []
}
}
// 等所有并发完成
resolveTotal(result)
})()
})
@ -237,6 +255,7 @@ export const getPages = async <T = any>(config: {
const result = await total
return result
}
/** bilibili API 标准响应 */
export interface BilibiliApiResponse {
code: number
@ -246,6 +265,7 @@ export interface BilibiliApiResponse {
data: any
result?: any
}
/**
* bilibili API
* @param apiPromise API Promise