refactor: 将订阅和插件过滤弹窗改为站点管理风格的下拉列表菜单

This commit is contained in:
jxxghp
2026-04-09 12:28:30 +08:00
parent 942ecc4c04
commit 6ef047050d
2 changed files with 192 additions and 122 deletions

View File

@@ -82,14 +82,25 @@ const filterOptions = computed(() => {
]
})
// 计算筛选按钮颜色
// 当前选中的筛选选项
const currentFilter = computed(() => {
return filterOptions.value.find(option => option.value === (subscribeStatusFilter.value || 'all'))
})
// 计算筛选按钮颜色 - 有名称筛选或状态筛选时高亮
const filterButtonColor = computed(() => {
if (subscribeFilter.value || (subscribeStatusFilter.value && subscribeStatusFilter.value !== 'all')) {
return 'primary'
return currentFilter.value?.color || 'primary'
}
return 'gray'
})
// 选择筛选选项
function selectFilter(value: string) {
subscribeStatusFilter.value = value
filterSubscribeDialog.value = false
}
// VMenu activator选择器
const filterActivator = computed(() => '[data-menu-activator="filter-btn"]')
const searchActivator = computed(() => '[data-menu-activator="search-btn"]')
@@ -200,44 +211,47 @@ onMounted(() => {
</VWindowItem>
</VWindow>
<!-- 订阅过滤弹窗 -->
<!-- 订阅过滤下拉菜单 -->
<Teleport to="body" v-if="filterSubscribeDialog">
<VMenu
v-model="filterSubscribeDialog"
width="25rem"
:close-on-content-click="false"
:activator="filterActivator"
location="bottom end"
>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-filter-multiple-outline" class="mr-2" />
{{ t('subscribe.filterSubscriptions') }}
</VCardTitle>
<VDialogCloseBtn @click="filterSubscribeDialog = false" />
</VCardItem>
<VCardText>
<VRow>
<!-- 名称筛选 -->
<VCol cols="6">
<VTextField v-model="subscribeFilter" :label="t('subscribe.name')" clearable density="comfortable" />
</VCol>
<!-- 状态筛选 -->
<VCol cols="6">
<VSelect
v-model="subscribeStatusFilter"
:items="filterOptions"
item-title="label"
item-value="value"
:label="t('common.status')"
density="comfortable"
clearable
/>
</VCol>
</VRow>
</VCardText>
<VCard min-width="220">
<!-- 名称搜索 -->
<div class="px-3 pt-3 pb-1">
<VTextField
v-model="subscribeFilter"
:placeholder="t('subscribe.name')"
prepend-inner-icon="mdi-magnify"
density="compact"
variant="outlined"
hide-details
clearable
/>
</div>
<VDivider class="mt-2" />
<!-- 状态筛选列表 -->
<VList density="compact" class="px-2 py-1">
<VListSubheader>{{ t('common.status') }}</VListSubheader>
<VListItem
v-for="option in filterOptions"
:key="option.value"
:active="(subscribeStatusFilter || 'all') === option.value"
@click="selectFilter(option.value)"
density="compact"
>
<template #prepend>
<VIcon :icon="option.icon" :color="option.color" size="small" />
</template>
<VListItemTitle>{{ option.label }}</VListItemTitle>
<template #append>
<VIcon v-if="(subscribeStatusFilter || 'all') === option.value" icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
</VList>
</VCard>
</VMenu>
</Teleport>

View File

@@ -219,6 +219,16 @@ const isFilterFormEmpty = computed(() => {
)
})
// 切换市场过滤器多选项
function toggleMarketFilter(field: 'author' | 'label' | 'repo', value: string) {
const index = filterForm[field].indexOf(value)
if (index > -1) {
filterForm[field].splice(index, 1)
} else {
filterForm[field].push(value)
}
}
// 插件过滤条件
const installedFilter = ref(null)
@@ -1289,113 +1299,159 @@ function onDragStartPlugin(evt: any) {
<template>
<div>
<!-- 过滤弹窗 -->
<!-- 已安装插件过滤下拉菜单 -->
<Teleport to="body" v-if="filterInstalledPluginDialog">
<VMenu
v-model="filterInstalledPluginDialog"
width="20rem"
:close-on-content-click="false"
:activator="'[data-menu-activator=installed-filter-btn]'"
location="bottom end"
>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-filter-multiple-outline" class="mr-2" />
{{ t('plugin.filterPlugins') }}
</VCardTitle>
<VDialogCloseBtn @click="filterInstalledPluginDialog = false" />
</VCardItem>
<VCardText>
<VRow>
<VCol cols="12">
<VCombobox
v-model="installedFilter"
:items="installedPluginNames"
:label="t('plugin.name')"
density="comfortable"
clearable
/>
</VCol>
<VCol cols="6">
<VSwitch v-model="enabledFilter" :label="t('plugin.running')" />
</VCol>
<VCol cols="6">
<VSwitch v-model="hasUpdateFilter" :label="t('plugin.hasNewVersion')" />
</VCol>
</VRow>
</VCardText>
<VCard min-width="220">
<!-- 名称搜索 -->
<div class="px-3 pt-3 pb-1">
<VCombobox
v-model="installedFilter"
:items="installedPluginNames"
:placeholder="t('plugin.name')"
prepend-inner-icon="mdi-magnify"
density="compact"
variant="outlined"
hide-details
clearable
/>
</div>
<VDivider class="mt-2" />
<!-- 快捷筛选 -->
<VList density="compact" class="px-2 py-1">
<VListSubheader>{{ t('common.filter') }}</VListSubheader>
<VListItem
:active="enabledFilter"
@click="enabledFilter = !enabledFilter"
density="compact"
>
<template #prepend>
<VIcon icon="mdi-play-circle" color="success" size="small" />
</template>
<VListItemTitle>{{ t('plugin.running') }}</VListItemTitle>
<template #append>
<VIcon v-if="enabledFilter" icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
<VListItem
:active="hasUpdateFilter"
@click="hasUpdateFilter = !hasUpdateFilter"
density="compact"
>
<template #prepend>
<VIcon icon="mdi-arrow-up-circle" color="info" size="small" />
</template>
<VListItemTitle>{{ t('plugin.hasNewVersion') }}</VListItemTitle>
<template #append>
<VIcon v-if="hasUpdateFilter" icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
</VList>
</VCard>
</VMenu>
</Teleport>
<!-- 插件市场过滤下拉菜单 -->
<Teleport to="body" v-if="filterMarketPluginDialog">
<VMenu
v-model="filterMarketPluginDialog"
width="25rem"
:close-on-content-click="false"
:activator="'[data-menu-activator=market-filter-btn]'"
location="bottom end"
max-height="80vh"
>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-filter-multiple-outline" class="mr-2" />
{{ t('plugin.filterPlugins') }}
</VCardTitle>
<VDialogCloseBtn @click="filterMarketPluginDialog = false" />
</VCardItem>
<VCardText>
<!-- 过滤表单 -->
<div v-if="isAppMarketLoaded">
<VRow>
<VCol cols="6">
<VTextField v-model="filterForm.name" density="comfortable" :label="t('plugin.name')" clearable />
</VCol>
<VCol v-if="authorFilterOptions.length > 0" cols="6">
<VSelect
v-model="filterForm.author"
:items="authorFilterOptions"
density="comfortable"
chips
:label="t('plugin.author')"
multiple
clearable
/>
</VCol>
<VCol v-if="labelFilterOptions.length > 0" cols="6">
<VSelect
v-model="filterForm.label"
:items="labelFilterOptions"
density="comfortable"
chips
:label="t('plugin.label')"
multiple
clearable
/>
</VCol>
<VCol v-if="repoFilterOptions.length > 0" cols="6">
<VSelect
v-model="filterForm.repo"
:items="repoFilterOptions"
density="comfortable"
chips
:label="t('plugin.repository')"
multiple
clearable
/>
</VCol>
<VCol v-if="sortOptions.length > 0" cols="6">
<VSelect
v-model="activeSort"
:items="sortOptions"
density="comfortable"
:label="t('plugin.sortTitle')"
/>
</VCol>
</VRow>
</div>
</VCardText>
<VCard min-width="240" max-width="300">
<!-- 名称搜索 -->
<div class="px-3 pt-3 pb-1">
<VTextField
v-model="filterForm.name"
:placeholder="t('plugin.name')"
prepend-inner-icon="mdi-magnify"
density="compact"
variant="outlined"
hide-details
clearable
/>
</div>
<VDivider class="mt-2" />
<!-- 排序 -->
<VList v-if="sortOptions.length > 0" density="compact" class="px-2 py-1">
<VListSubheader>{{ t('plugin.sortTitle') }}</VListSubheader>
<VListItem
v-for="option in sortOptions"
:key="option.value"
:active="(activeSort || 'count') === option.value"
@click="activeSort = option.value"
density="compact"
>
<VListItemTitle>{{ option.title }}</VListItemTitle>
<template #append>
<VIcon v-if="(activeSort || 'count') === option.value" icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
</VList>
<!-- 作者筛选 -->
<template v-if="authorFilterOptions.length > 0">
<VDivider />
<VList density="compact" class="px-2 py-1">
<VListSubheader>{{ t('plugin.author') }}</VListSubheader>
<VListItem
v-for="author in authorFilterOptions"
:key="author"
:active="filterForm.author.includes(author)"
@click="toggleMarketFilter('author', author)"
density="compact"
>
<VListItemTitle>{{ author }}</VListItemTitle>
<template #append>
<VIcon v-if="filterForm.author.includes(author)" icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
</VList>
</template>
<!-- 标签筛选 -->
<template v-if="labelFilterOptions.length > 0">
<VDivider />
<VList density="compact" class="px-2 py-1">
<VListSubheader>{{ t('plugin.label') }}</VListSubheader>
<VListItem
v-for="label in labelFilterOptions"
:key="label"
:active="filterForm.label.includes(label)"
@click="toggleMarketFilter('label', label)"
density="compact"
>
<VListItemTitle>{{ label }}</VListItemTitle>
<template #append>
<VIcon v-if="filterForm.label.includes(label)" icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
</VList>
</template>
<!-- 仓库筛选 -->
<template v-if="repoFilterOptions.length > 0">
<VDivider />
<VList density="compact" class="px-2 py-1">
<VListSubheader>{{ t('plugin.repository') }}</VListSubheader>
<VListItem
v-for="repo in repoFilterOptions"
:key="repo"
:active="filterForm.repo.includes(repo)"
@click="toggleMarketFilter('repo', repo)"
density="compact"
>
<VListItemTitle class="text-truncate" style="max-width: 220px">{{ repo }}</VListItemTitle>
<template #append>
<VIcon v-if="filterForm.repo.includes(repo)" icon="mdi-check" color="primary" size="small" />
</template>
</VListItem>
</VList>
</template>
</VCard>
</VMenu>
</Teleport>