diff --git a/registry/lib/components/utils/black-list/BlockListSettings.vue b/registry/lib/components/utils/black-list/BlockListSettings.vue
new file mode 100644
index 000000000..1ea518109
--- /dev/null
+++ b/registry/lib/components/utils/black-list/BlockListSettings.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+ 添加到黑名单
+
+
+
+ 添加
+
+
+
+
+ 黑名单列表
+
+
+ 点击×图标可以删除名单.
+
+
+
+
+
+
+
+
+
diff --git a/registry/lib/components/utils/black-list/Settings.vue b/registry/lib/components/utils/black-list/Settings.vue
new file mode 100644
index 000000000..6617084e2
--- /dev/null
+++ b/registry/lib/components/utils/black-list/Settings.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
diff --git a/registry/lib/components/utils/black-list/common.ts b/registry/lib/components/utils/black-list/common.ts
new file mode 100644
index 000000000..a54609c41
--- /dev/null
+++ b/registry/lib/components/utils/black-list/common.ts
@@ -0,0 +1 @@
+export const BlockListDataKey = 'blokList.data'
diff --git a/registry/lib/components/utils/black-list/index.ts b/registry/lib/components/utils/black-list/index.ts
new file mode 100644
index 000000000..8ab3ddf19
--- /dev/null
+++ b/registry/lib/components/utils/black-list/index.ts
@@ -0,0 +1,80 @@
+import { ComponentMetadata } from '@/components/types'
+import { mainSiteUrls } from '@/core/utils/urls'
+import { allMutationsOn } from '@/core/observer'
+import { selectAll } from '@/core/spin-query'
+import { registerData, getData } from '@/plugins/data'
+import { BlockListDataKey } from './common'
+
+const name = 'blackList'
+
+const entry = async ({ settings: { options } }) => {
+ const blockListData = {
+ up: options.up,
+ upRegex: options.upRegex,
+ }
+
+ registerData(BlockListDataKey, blockListData)
+ const billGrid = await selectAll('.bili-grid')
+ allMutationsOn(billGrid, async () => {
+ const videos = await selectAll('.bili-video-card')
+ if (!videos) {
+ return
+ }
+ const blockList = getData(BlockListDataKey)
+ const upRegex = blockList[0].upRegex.map(e => new RegExp(e))
+ videos.forEach(video => {
+ const authorElement = (video as unknown as HTMLElement).querySelector('.bili-video-card__info--author')
+ const titleElement = (video as unknown as HTMLElement).querySelector('.bili-video-card__info--tit > a')
+ if (authorElement != null) {
+ const author = authorElement.innerHTML
+ if (blockList[0].up.indexOf(author) !== -1) {
+ const image = (video as unknown as HTMLElement).querySelector('.v-img.bili-video-card__cover')
+ image.innerHTML = ''
+ authorElement.innerHTML = ''
+ titleElement.innerHTML = ''
+ } else {
+ for (const i in upRegex) {
+ if (upRegex[i].test(author)) {
+ const image = (video as unknown as HTMLElement).querySelector('.v-img.bili-video-card__cover')
+ image.innerHTML = ''
+ authorElement.innerHTML = ''
+ titleElement.innerHTML = ''
+ break
+ }
+ }
+ }
+ }
+ })
+ })
+}
+
+export const component: ComponentMetadata = {
+ name,
+ entry,
+ // reload: entry,
+ extraOptions: () => import('./Settings.vue').then(m => m.default),
+ options: {
+ up: {
+ displayName: 'up主名称',
+ defaultValue: [],
+ hidden: true,
+ },
+ upRegex: {
+ displayName: '正则匹配up主名称',
+ defaultValue: [],
+ hidden: true,
+ },
+ },
+ displayName: '屏蔽黑名单up主',
+ tags: [
+ componentsTags.utils,
+ ],
+ description: {
+ 'zh-CN': '屏蔽黑名单up主',
+ },
+ author: {
+ name: 'snowraincloud',
+ link: 'https://github.com/snowraincloud',
+ },
+ urlInclude: mainSiteUrls,
+}
diff --git a/registry/lib/components/utils/black-list/vm.ts b/registry/lib/components/utils/black-list/vm.ts
new file mode 100644
index 000000000..5c2254183
--- /dev/null
+++ b/registry/lib/components/utils/black-list/vm.ts
@@ -0,0 +1,83 @@
+import { getComponentSettings } from '@/core/settings'
+import { mountVueComponent } from '@/core/utils'
+import { addData, getData } from '@/plugins/data'
+import { BlockListDataKey } from './common'
+
+type SettingsVmType = Vue & {
+ toggle: () => void
+ triggerElement: HTMLElement
+ list : string[]
+ save : (items:string[]) => void
+ titleName: string
+}
+let nameSettingsVM: SettingsVmType
+let regexSettingsVm: SettingsVmType
+
+const blackListOptions = getComponentSettings('blackList').options as {
+ up: string[]
+ upRegex: string[]
+}
+
+export const setNameProps = (element: HTMLElement) => {
+ if (!nameSettingsVM) {
+ return
+ }
+ nameSettingsVM.triggerElement = element
+ const blockList = getData(BlockListDataKey)
+ nameSettingsVM.list = lodash.cloneDeep(blockList[0].up)
+ nameSettingsVM.save = (items:string[]) => {
+ addData(BlockListDataKey, data => {
+ data.up = items
+ })
+ blackListOptions.up = items
+ }
+ nameSettingsVM.titleName = '精确匹配'
+}
+export const setRegexProps = (element: HTMLElement) => {
+ if (!regexSettingsVm) {
+ return
+ }
+ regexSettingsVm.triggerElement = element
+ const blockList = getData(BlockListDataKey)
+ regexSettingsVm.list = lodash.cloneDeep(blockList[0].upRegex)
+ regexSettingsVm.save = (items:string[]) => {
+ addData(BlockListDataKey, data => {
+ data.upRegex = items
+ })
+ blackListOptions.upRegex = items
+ }
+ regexSettingsVm.titleName = '正则匹配'
+}
+
+export const loadNameSettings = async () => {
+ if (nameSettingsVM) {
+ return false
+ }
+ const blockListSettings = await import('./BlockListSettings.vue').then(m => m.default)
+ nameSettingsVM = mountVueComponent(blockListSettings)
+ document.body.insertAdjacentElement('beforeend', nameSettingsVM.$el)
+ return true
+}
+
+export const loadRegexSettings = async () => {
+ if (regexSettingsVm) {
+ return false
+ }
+ const blockListSettings = await import('./BlockListSettings.vue').then(m => m.default)
+ regexSettingsVm = mountVueComponent(blockListSettings)
+ document.body.insertAdjacentElement('beforeend', regexSettingsVm.$el)
+ return true
+}
+export const toggleNameSettings = async () => {
+ if (!nameSettingsVM) {
+ await loadNameSettings()
+ }
+ nameSettingsVM?.toggle()
+}
+
+export const toggleRegexSettings = async () => {
+ if (!regexSettingsVm) {
+ await loadRegexSettings()
+ }
+ regexSettingsVm?.toggle()
+}