fix 插件过滤 & 标签

This commit is contained in:
jxxghp
2024-04-28 13:40:49 +08:00
parent f7502d0d18
commit c1bb66cc9d
4 changed files with 141 additions and 13 deletions

View File

@@ -407,6 +407,8 @@ export interface Plugin {
plugin_desc?: string plugin_desc?: string
// 插件图标 // 插件图标
plugin_icon?: string plugin_icon?: string
// 插件标签,多个以,分隔
plugin_label?: string
// 插件版本 // 插件版本
plugin_version?: string plugin_version?: string
// 插件作者 // 插件作者

View File

@@ -43,6 +43,12 @@ const imageLoadError = ref(false)
// 更新日志弹窗 // 更新日志弹窗
const releaseDialog = ref(false) const releaseDialog = ref(false)
// 计算插件标签
const pluginLabels = computed(() => {
if (!props.plugin?.plugin_label) return []
return props.plugin.plugin_label.split(',')
})
// 图片加载完成 // 图片加载完成
async function imageLoaded() { async function imageLoaded() {
isImageLoaded.value = true isImageLoaded.value = true
@@ -183,7 +189,12 @@ const dropdownItems = ref([
<span class="text-sm text-gray-500">v{{ props.plugin?.plugin_version }}</span> <span class="text-sm text-gray-500">v{{ props.plugin?.plugin_version }}</span>
</VCardTitle> </VCardTitle>
<VCardText class="pb-2"> <VCardText class="pb-2">
{{ props.plugin?.plugin_desc }} <div>{{ props.plugin?.plugin_desc }}</div>
<div>
<VChip v-for="label in pluginLabels" variant="tonal" size="small" class="me-1 my-1" color="info" label>
{{ label }}
</VChip>
</div>
</VCardText> </VCardText>
<VCardText class="flex items-center justify-start pb-2"> <VCardText class="flex items-center justify-start pb-2">
<span> <span>

View File

@@ -7,6 +7,7 @@ import PluginAppCard from '@/components/cards/PluginAppCard.vue'
import PluginCard from '@/components/cards/PluginCard.vue' import PluginCard from '@/components/cards/PluginCard.vue'
import noImage from '@images/logos/plugin.png' import noImage from '@images/logos/plugin.png'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { isNullOrEmptyObject } from '@/@core/utils'
// 显示器宽度 // 显示器宽度
const display = useDisplay() const display = useDisplay()
@@ -17,6 +18,9 @@ const dataList = ref<Plugin[]>([])
// 未安装插件列表 // 未安装插件列表
const uninstalledList = ref<Plugin[]>([]) const uninstalledList = ref<Plugin[]>([])
// 插件市场插件列表
const marketList = ref<Plugin[]>([])
// 是否刷新过 // 是否刷新过
const isRefreshed = ref(false) const isRefreshed = ref(false)
@@ -50,6 +54,41 @@ const progressDialog = ref(false)
// 进度框文本 // 进度框文本
const progressText = ref('正在安装插件...') const progressText = ref('正在安装插件...')
// 过滤表单
const filterForm = reactive({
// 名称
name: [] as string[],
// 作者
author: [] as string[],
// 标签
label: [] as string[],
// 插件库
repo: [] as string[],
})
// 名称过滤项
const nameFilterOptions = ref<string[]>([])
// 作者过滤项
const authorFilterOptions = ref<string[]>([])
// 标签过滤项
const labelFilterOptions = ref<string[]>([])
// 插件库过滤项
const repoFilterOptions = ref<string[]>([])
// 初始化过滤选项
function initOptions(item: Plugin) {
const optionValue = (options: Array<string>, value: string | undefined) => {
value && !options.includes(value) && options.push(value)
}
const optionMutipleValue = (options: Array<string>, value: string | undefined) => {
value && value.split(',').forEach(v => !options.includes(v) && options.push(v))
}
optionValue(nameFilterOptions.value, item.plugin_name)
optionValue(authorFilterOptions.value, item.plugin_author)
optionMutipleValue(labelFilterOptions.value, item.plugin_label)
optionValue(repoFilterOptions.value, item.repo_url)
}
// 关闭插件市场窗口 // 关闭插件市场窗口
function pluginDialogClose() { function pluginDialogClose() {
PluginAppDialog.value = false PluginAppDialog.value = false
@@ -123,7 +162,11 @@ function pluginIcon(item: Plugin) {
const filterPlugins = computed(() => { const filterPlugins = computed(() => {
const all_list = [...dataList.value, ...uninstalledList.value] const all_list = [...dataList.value, ...uninstalledList.value]
return all_list.filter((item: Plugin) => { return all_list.filter((item: Plugin) => {
return item.plugin_name?.includes(keyword.value) || item.plugin_desc?.includes(keyword.value) // 需要忽略大小写
return (
item.plugin_name?.toLowerCase().includes(keyword.value.toLowerCase()) ||
item.plugin_desc?.toLowerCase().includes(keyword.value.toLowerCase())
)
}) })
}) })
@@ -167,6 +210,11 @@ async function fetchUninstalledPlugins() {
} }
} }
} }
// 更新插件市场列表
// 排除已安装且有更新的,上面的问题在于“本地存在未安装的旧版本插件且云端有更新时”不会在插件市场展示
marketList.value = uninstalledList.value.filter(item => !(item.has_update && item.installed))
// 初始化过滤选项
marketList.value.forEach(initOptions)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
@@ -189,11 +237,32 @@ function refreshData() {
// 对uninstalledList进行排序按PluginStatistics倒序 // 对uninstalledList进行排序按PluginStatistics倒序
const sortedUninstalledList = computed(() => { const sortedUninstalledList = computed(() => {
//const list = uninstalledList.value.filter(item => !item.has_update) // 匹配过滤函数
// 排除已安装且有更新的,上面的问题在于“本地存在未安装的旧版本插件且云端有更新时”不会在插件市场展示 const match = (filter: Array<string>, value: string | undefined) =>
const list = uninstalledList.value.filter(item => !(item.has_update && item.installed)) filter.length === 0 || (value && filter.includes(value))
if (PluginStatistics.value.length === 0) return list const matchMultiple = (filter: Array<string>, value: string | undefined) =>
return list.sort((a, b) => { filter.length === 0 || (value && value.split(',').some(v => filter.includes(v)))
// 过滤后的数据列表
const ret_list: Plugin[] = []
// 过滤
marketList.value.forEach(value => {
if (value) {
if (
match(filterForm.name, value.plugin_name) &&
match(filterForm.author, value.plugin_author) &&
matchMultiple(filterForm.label, value.plugin_label) &&
match(filterForm.repo, value.repo_url)
) {
ret_list.push(value)
}
}
})
// 按照统计数据排序
if (isNullOrEmptyObject(PluginStatistics.value)) return ret_list
return ret_list.sort((a, b) => {
return PluginStatistics.value[b.id || '0'] - PluginStatistics.value[a.id || '0'] return PluginStatistics.value[b.id || '0'] - PluginStatistics.value[a.id || '0']
}) })
}) })
@@ -236,15 +305,12 @@ onBeforeMount(() => {
:z-index="1010" :z-index="1010"
transition="dialog-bottom-transition" transition="dialog-bottom-transition"
> >
<!-- Dialog Content -->
<VCard> <VCard>
<!-- Toolbar --> <!-- 标题栏 -->
<div> <div>
<VToolbar color="primary"> <VToolbar color="primary">
<VToolbarTitle>插件市场</VToolbarTitle> <VToolbarTitle>插件市场</VToolbarTitle>
<VSpacer /> <VSpacer />
<VToolbarItems> <VToolbarItems>
<VBtn size="x-large" @click="pluginDialogClose"> <VBtn size="x-large" @click="pluginDialogClose">
<VIcon color="white" icon="mdi-close" /> <VIcon color="white" icon="mdi-close" />
@@ -254,6 +320,55 @@ onBeforeMount(() => {
</div> </div>
<VCardText> <VCardText>
<LoadingBanner v-if="!isAppMarketLoaded" class="mt-12" /> <LoadingBanner v-if="!isAppMarketLoaded" class="mt-12" />
<!-- 过滤表单 -->
<div v-if="isAppMarketLoaded" class="bg-transparent mb-3 shadow-none">
<VRow>
<VCol v-if="nameFilterOptions.length > 0" cols="6" md="3">
<VSelect
v-model="filterForm.name"
:items="nameFilterOptions"
size="small"
density="compact"
chips
label="名称"
multiple
/>
</VCol>
<VCol v-if="authorFilterOptions.length > 0" cols="6" md="3">
<VSelect
v-model="filterForm.author"
:items="authorFilterOptions"
size="small"
density="compact"
chips
label="作者"
multiple
/>
</VCol>
<VCol v-if="labelFilterOptions.length > 0" cols="6" md="3">
<VSelect
v-model="filterForm.label"
:items="labelFilterOptions"
size="small"
density="compact"
chips
label="标签"
multiple
/>
</VCol>
<VCol v-if="repoFilterOptions.length > 0" cols="6" md="3">
<VSelect
v-model="filterForm.repo"
:items="repoFilterOptions"
size="small"
density="compact"
chips
label="插件库"
multiple
/>
</VCol>
</VRow>
</div>
<div v-if="isAppMarketLoaded" class="grid gap-4 grid-plugin-card"> <div v-if="isAppMarketLoaded" class="grid gap-4 grid-plugin-card">
<PluginAppCard <PluginAppCard
v-for="data in sortedUninstalledList" v-for="data in sortedUninstalledList"

View File

@@ -31,7 +31,7 @@ const defaultFilterRules = ref({
include: '', include: '',
exclude: '', exclude: '',
min_seeders: 0, min_seeders: 0,
min_seeders_time: 0 min_seeders_time: 0,
}) })
// 媒体信息数据源字典 // 媒体信息数据源字典
@@ -320,7 +320,7 @@ onMounted(() => {
chips chips
:items="mediaSourcesDict" :items="mediaSourcesDict"
label="当前使用数据源" label="当前使用数据源"
hint="选中多项时会同时展示来自不同数据源的搜索结果" hint="选中多项时会同时展示来自不同数据源的搜索结果,选择的数据源顺序将会搜索结果的排序"
/> />
</VCol> </VCol>
</VRow> </VRow>