mirror of https://github.com/halo-dev/halo-admin
feat: add plugin filters support (#629)
#### What type of PR is this? /kind feature /kind improvement /milestone 2.0 #### What this PR does / why we need it: 添加插件管理筛选的支持。适配 https://github.com/halo-dev/halo/pull/2489 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2470 #### Special notes for your reviewer: /cc @halo-dev/sig-halo /cc @halo-dev/sig-halo-console 测试方式: 1. Halo 需要切换到 https://github.com/halo-dev/halo/pull/2489 PR 的分支。 2. Console 需要 `pnpm install` 3. 安装若干插件,测试筛选和排序。 #### Does this PR introduce a user-facing change? ```release-note 插件管理支持筛选 ```pull/632/head
parent
13b4094468
commit
bd1472251f
|
@ -34,7 +34,7 @@
|
|||
"@formkit/themes": "1.0.0-beta.10",
|
||||
"@formkit/vue": "1.0.0-beta.10",
|
||||
"@halo-dev/admin-shared": "workspace:*",
|
||||
"@halo-dev/api-client": "^0.0.33",
|
||||
"@halo-dev/api-client": "^0.0.34",
|
||||
"@halo-dev/components": "workspace:*",
|
||||
"@halo-dev/richtext-editor": "^0.0.0-alpha.7",
|
||||
"@tiptap/extension-character-count": "2.0.0-beta.31",
|
||||
|
|
|
@ -14,7 +14,7 @@ importers:
|
|||
'@formkit/themes': 1.0.0-beta.10
|
||||
'@formkit/vue': 1.0.0-beta.10
|
||||
'@halo-dev/admin-shared': workspace:*
|
||||
'@halo-dev/api-client': ^0.0.33
|
||||
'@halo-dev/api-client': ^0.0.34
|
||||
'@halo-dev/components': workspace:*
|
||||
'@halo-dev/richtext-editor': ^0.0.0-alpha.7
|
||||
'@iconify-json/mdi': ^1.1.33
|
||||
|
@ -101,7 +101,7 @@ importers:
|
|||
'@formkit/themes': 1.0.0-beta.10_tailwindcss@3.1.8
|
||||
'@formkit/vue': 1.0.0-beta.10_k5hp3txgeyj6le63abiyc7wx3u
|
||||
'@halo-dev/admin-shared': link:packages/shared
|
||||
'@halo-dev/api-client': 0.0.33
|
||||
'@halo-dev/api-client': 0.0.34
|
||||
'@halo-dev/components': link:packages/components
|
||||
'@halo-dev/richtext-editor': 0.0.0-alpha.7_vue@3.2.40
|
||||
'@tiptap/extension-character-count': 2.0.0-beta.31
|
||||
|
@ -1886,8 +1886,8 @@ packages:
|
|||
- windicss
|
||||
dev: false
|
||||
|
||||
/@halo-dev/api-client/0.0.33:
|
||||
resolution: {integrity: sha512-L0U11BO0rInJ49JjH/aynFFj28+vnBDXtDKDMNCnaZNo4ijweqdU8q4S6lUo6okGbznYlZ9fWDPDHvS7EKqHKA==}
|
||||
/@halo-dev/api-client/0.0.34:
|
||||
resolution: {integrity: sha512-WOEbyRjPSASH1tyQjQQU/RjXKtRPSD+L6erAWLUfy7BIsyCAMSBdkF3auBWaevUt08uyCBeI3ln+sP+VPyFM0A==}
|
||||
dev: false
|
||||
|
||||
/@halo-dev/richtext-editor/0.0.0-alpha.7_vue@3.2.40:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import {
|
||||
IconAddCircle,
|
||||
IconArrowDown,
|
||||
IconCloseCircle,
|
||||
IconPlug,
|
||||
VButton,
|
||||
VCard,
|
||||
|
@ -9,15 +10,23 @@ import {
|
|||
VPageHeader,
|
||||
VPagination,
|
||||
VSpace,
|
||||
VTag,
|
||||
} from "@halo-dev/components";
|
||||
import PluginListItem from "./components/PluginListItem.vue";
|
||||
import PluginInstallModal from "./components/PluginInstallModal.vue";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import type { Plugin } from "@halo-dev/api-client";
|
||||
import type { PluginList } from "@halo-dev/api-client";
|
||||
|
||||
const plugins = ref<Plugin[]>([] as Plugin[]);
|
||||
const plugins = ref<PluginList>({
|
||||
page: 1,
|
||||
size: 20,
|
||||
total: 0,
|
||||
items: [],
|
||||
first: true,
|
||||
last: false,
|
||||
hasNext: false,
|
||||
hasPrevious: false,
|
||||
});
|
||||
const loading = ref(false);
|
||||
const pluginInstall = ref(false);
|
||||
const keyword = ref("");
|
||||
|
@ -26,34 +35,87 @@ const handleFetchPlugins = async () => {
|
|||
try {
|
||||
loading.value = true;
|
||||
|
||||
const fieldSelector: Array<string> = [];
|
||||
const { data } = await apiClient.plugin.listPlugins({
|
||||
page: plugins.value.page,
|
||||
size: plugins.value.size,
|
||||
keyword: keyword.value,
|
||||
enabled: selectedEnabledItem.value?.value,
|
||||
sort: [selectedSortItem.value?.value].filter(
|
||||
(item) => !!item
|
||||
) as string[],
|
||||
});
|
||||
|
||||
if (keyword.value) {
|
||||
fieldSelector.push(`name=${keyword.value}`);
|
||||
}
|
||||
|
||||
const { data } =
|
||||
await apiClient.extension.plugin.listpluginHaloRunV1alpha1Plugin({
|
||||
page: 0,
|
||||
size: 0,
|
||||
fieldSelector,
|
||||
});
|
||||
plugins.value = data.items;
|
||||
plugins.value = data;
|
||||
} catch (e) {
|
||||
console.error("Fail to fetch plugins", e);
|
||||
console.error("Failed to fetch plugins", e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => keyword.value,
|
||||
() => {
|
||||
handleFetchPlugins();
|
||||
}
|
||||
);
|
||||
const handlePaginationChange = ({
|
||||
page,
|
||||
size,
|
||||
}: {
|
||||
page: number;
|
||||
size: number;
|
||||
}) => {
|
||||
plugins.value.page = page;
|
||||
plugins.value.size = size;
|
||||
handleFetchPlugins();
|
||||
};
|
||||
|
||||
onMounted(handleFetchPlugins);
|
||||
|
||||
// Filters
|
||||
interface EnabledItem {
|
||||
label: string;
|
||||
value?: boolean;
|
||||
}
|
||||
|
||||
interface SortItem {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const EnabledItems: EnabledItem[] = [
|
||||
{
|
||||
label: "全部",
|
||||
value: undefined,
|
||||
},
|
||||
{
|
||||
label: "已启用",
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
label: "未启用",
|
||||
value: false,
|
||||
},
|
||||
];
|
||||
|
||||
const SortItems: SortItem[] = [
|
||||
{
|
||||
label: "较近安装",
|
||||
value: "creationTimestamp,desc",
|
||||
},
|
||||
{
|
||||
label: "较早安装",
|
||||
value: "creationTimestamp,asc",
|
||||
},
|
||||
];
|
||||
|
||||
const selectedEnabledItem = ref<EnabledItem>();
|
||||
const selectedSortItem = ref<SortItem>();
|
||||
|
||||
function handleEnabledItemChange(enabledItem: EnabledItem) {
|
||||
selectedEnabledItem.value = enabledItem;
|
||||
handleFetchPlugins();
|
||||
}
|
||||
|
||||
function handleSortItemChange(sortItem?: SortItem) {
|
||||
selectedSortItem.value = sortItem;
|
||||
handleFetchPlugins();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<PluginInstallModal
|
||||
|
@ -87,12 +149,39 @@ onMounted(handleFetchPlugins);
|
|||
<div
|
||||
class="relative flex flex-col items-start sm:flex-row sm:items-center"
|
||||
>
|
||||
<div class="flex w-full flex-1 sm:w-auto">
|
||||
<div class="flex w-full flex-1 items-center gap-2 sm:w-auto">
|
||||
<FormKit
|
||||
v-model="keyword"
|
||||
placeholder="输入关键词搜索"
|
||||
type="text"
|
||||
@keyup.enter="
|
||||
handlePaginationChange({ page: 1, size: plugins.size })
|
||||
"
|
||||
></FormKit>
|
||||
<div
|
||||
v-if="selectedEnabledItem?.value !== undefined"
|
||||
class="group flex cursor-pointer items-center justify-center gap-1 rounded-full bg-gray-200 px-2 py-1 hover:bg-gray-300"
|
||||
>
|
||||
<span class="text-xs text-gray-600 group-hover:text-gray-900">
|
||||
启用状态:{{ selectedEnabledItem.label }}
|
||||
</span>
|
||||
<IconCloseCircle
|
||||
class="h-4 w-4 text-gray-600"
|
||||
@click="handleEnabledItemChange(EnabledItems[0])"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedSortItem"
|
||||
class="group flex cursor-pointer items-center justify-center gap-1 rounded-full bg-gray-200 px-2 py-1 hover:bg-gray-300"
|
||||
>
|
||||
<span class="text-xs text-gray-600 group-hover:text-gray-900">
|
||||
排序:{{ selectedSortItem.label }}
|
||||
</span>
|
||||
<IconCloseCircle
|
||||
class="h-4 w-4 text-gray-600"
|
||||
@click="handleSortItemChange()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex sm:mt-0">
|
||||
<VSpace spacing="lg">
|
||||
|
@ -109,84 +198,22 @@ onMounted(handleFetchPlugins);
|
|||
<div class="w-52 p-4">
|
||||
<ul class="space-y-1">
|
||||
<li
|
||||
v-for="(enabledItem, index) in EnabledItems"
|
||||
:key="index"
|
||||
v-close-popper
|
||||
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
|
||||
:class="{
|
||||
'bg-gray-100':
|
||||
selectedEnabledItem?.value === enabledItem.value,
|
||||
}"
|
||||
@click="handleEnabledItemChange(enabledItem)"
|
||||
>
|
||||
<span class="truncate">已启用</span>
|
||||
</li>
|
||||
<li
|
||||
v-close-popper
|
||||
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
|
||||
>
|
||||
<span class="truncate">未启用</span>
|
||||
<span class="truncate">{{ enabledItem.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
<div
|
||||
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
|
||||
>
|
||||
<span class="mr-0.5">类别</span>
|
||||
<span>
|
||||
<IconArrowDown />
|
||||
</span>
|
||||
</div>
|
||||
<FloatingDropdown>
|
||||
<div
|
||||
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
|
||||
>
|
||||
<span class="mr-0.5">提供方</span>
|
||||
<span>
|
||||
<IconArrowDown />
|
||||
</span>
|
||||
</div>
|
||||
<template #popper>
|
||||
<div class="h-96 w-80 p-4">
|
||||
<div class="bg-white">
|
||||
<!--TODO: Auto Focus-->
|
||||
<FormKit
|
||||
placeholder="输入关键词搜索"
|
||||
type="text"
|
||||
></FormKit>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<ul class="divide-y divide-gray-200" role="list">
|
||||
<li class="cursor-pointer py-4 hover:bg-gray-50">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
class="h-4 w-4 rounded border-gray-300 text-indigo-600"
|
||||
type="checkbox"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
alt="halo-dev"
|
||||
class="h-10 w-10 rounded"
|
||||
src="https://halo.run/logo"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p
|
||||
class="truncate text-sm font-medium text-gray-900"
|
||||
>
|
||||
Halo
|
||||
</p>
|
||||
<p class="truncate text-sm text-gray-500">
|
||||
https://halo.run
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<VTag>2 个</VTag>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
<FloatingDropdown>
|
||||
<div
|
||||
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
|
||||
|
@ -200,16 +227,13 @@ onMounted(handleFetchPlugins);
|
|||
<div class="w-72 p-4">
|
||||
<ul class="space-y-1">
|
||||
<li
|
||||
v-for="(sortItem, index) in SortItems"
|
||||
:key="index"
|
||||
v-close-popper
|
||||
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
|
||||
@click="handleSortItemChange(sortItem)"
|
||||
>
|
||||
<span class="truncate">较近安装</span>
|
||||
</li>
|
||||
<li
|
||||
v-close-popper
|
||||
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
|
||||
>
|
||||
<span class="truncate">较晚安装</span>
|
||||
<span class="truncate">{{ sortItem.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -222,7 +246,7 @@ onMounted(handleFetchPlugins);
|
|||
</template>
|
||||
|
||||
<VEmpty
|
||||
v-if="!plugins.length && !loading"
|
||||
v-if="!plugins.total && !loading"
|
||||
message="当前没有已安装的插件,你可以尝试刷新或者安装新插件"
|
||||
title="当前没有已安装的插件"
|
||||
>
|
||||
|
@ -248,14 +272,20 @@ onMounted(handleFetchPlugins);
|
|||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
<li v-for="(plugin, index) in plugins" :key="index">
|
||||
<li v-for="(plugin, index) in plugins.items" :key="index">
|
||||
<PluginListItem :plugin="plugin" />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
<VPagination :page="1" :size="10" :total="20" />
|
||||
<VPagination
|
||||
:page="plugins.page"
|
||||
:size="plugins.size"
|
||||
:total="plugins.total"
|
||||
:size-options="[20, 30, 50, 100]"
|
||||
@change="handlePaginationChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</VCard>
|
||||
|
|
Loading…
Reference in New Issue