mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
commit
406b2f6318
156
registry/lib/components/feeds/group-filter/FilterPanel.vue
Normal file
156
registry/lib/components/feeds/group-filter/FilterPanel.vue
Normal 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) {
|
||||||
|
// 从username获得tag
|
||||||
|
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>
|
||||||
1
registry/lib/components/feeds/group-filter/index.md
Normal file
1
registry/lib/components/feeds/group-filter/index.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
按照关注分组筛选动态
|
||||||
34
registry/lib/components/feeds/group-filter/index.ts
Normal file
34
registry/lib/components/feeds/group-filter/index.ts
Normal 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],
|
||||||
|
})
|
||||||
@ -194,27 +194,45 @@ export const responsiveGetPages = <T = any>(config: {
|
|||||||
responsivePromise = new Promise(resolveResponsive => {
|
responsivePromise = new Promise(resolveResponsive => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
const { api, getList, getTotal } = config
|
const { api, getList, getTotal } = config
|
||||||
let page = 1
|
|
||||||
let total = Infinity
|
let total = Infinity
|
||||||
const result = []
|
const result: T[] = []
|
||||||
while (result.length < total) {
|
|
||||||
const json = await api(page)
|
const fetchPage = async (p: number): Promise<T[]> => {
|
||||||
|
const json = await api(p)
|
||||||
if (json.code !== 0) {
|
if (json.code !== 0) {
|
||||||
console.warn(
|
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,
|
api,
|
||||||
)
|
)
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
const list = getList(json)
|
return getList(json)
|
||||||
result.push(...list)
|
}
|
||||||
if (page === 1) {
|
|
||||||
resolveResponsive(result)
|
// 请求第一次获得total
|
||||||
}
|
const firstReq = await api(1)
|
||||||
page++
|
result.push(...getList(firstReq))
|
||||||
if (total === Infinity) {
|
|
||||||
total = getTotal(json)
|
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)
|
resolveTotal(result)
|
||||||
})()
|
})()
|
||||||
})
|
})
|
||||||
@ -237,6 +255,7 @@ export const getPages = async <T = any>(config: {
|
|||||||
const result = await total
|
const result = await total
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
/** bilibili API 标准响应 */
|
/** bilibili API 标准响应 */
|
||||||
export interface BilibiliApiResponse {
|
export interface BilibiliApiResponse {
|
||||||
code: number
|
code: number
|
||||||
@ -246,6 +265,7 @@ export interface BilibiliApiResponse {
|
|||||||
data: any
|
data: any
|
||||||
result?: any
|
result?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进行 bilibili API 标准响应处理
|
* 进行 bilibili API 标准响应处理
|
||||||
* @param apiPromise 运行中的 API Promise
|
* @param apiPromise 运行中的 API Promise
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user