feat: refine post filtering

pull/3445/head
Ryan Wang 2022-09-10 00:32:46 +08:00
parent ea10523dd5
commit bcd997231a
2 changed files with 150 additions and 11 deletions

View File

@ -9,6 +9,7 @@ import {
IconEyeOff, IconEyeOff,
IconSettings, IconSettings,
IconTeam, IconTeam,
IconCloseCircle,
useDialog, useDialog,
VButton, VButton,
VCard, VCard,
@ -21,7 +22,14 @@ import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSel
import PostSettingModal from "./components/PostSettingModal.vue"; import PostSettingModal from "./components/PostSettingModal.vue";
import PostTag from "../posts/tags/components/PostTag.vue"; import PostTag from "../posts/tags/components/PostTag.vue";
import { onMounted, ref, watch, watchEffect } from "vue"; import { onMounted, ref, watch, watchEffect } from "vue";
import type { ListedPostList, Post, PostRequest } from "@halo-dev/api-client"; import type {
User,
Category,
ListedPostList,
Post,
PostRequest,
Tag,
} from "@halo-dev/api-client";
import { apiClient } from "@halo-dev/admin-shared"; import { apiClient } from "@halo-dev/admin-shared";
import { formatDatetime } from "@/utils/date"; import { formatDatetime } from "@/utils/date";
import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category"; import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
@ -74,10 +82,29 @@ const handleFetchPosts = async () => {
); );
} }
let categories: string[] | undefined;
let tags: string[] | undefined;
let contributors: string[] | undefined;
if (selectedCategoryFilterItem.value) {
categories = [selectedCategoryFilterItem.value.metadata.name];
}
if (selectedTagFilterItem.value) {
tags = [selectedTagFilterItem.value.metadata.name];
}
if (selectedContributorItem.value) {
contributors = [selectedContributorItem.value.metadata.name];
}
const { data } = await apiClient.post.listPosts({ const { data } = await apiClient.post.listPosts({
page: posts.value.page, page: posts.value.page,
size: posts.value.size, size: posts.value.size,
labelSelector, labelSelector,
categories,
tags,
contributors,
}); });
posts.value = data; posts.value = data;
} catch (e) { } catch (e) {
@ -266,6 +293,9 @@ const PhaseFilterItems: FilterItem[] = [
const selectedVisibleFilterItem = ref<FilterItem>(VisibleFilterItems[0]); const selectedVisibleFilterItem = ref<FilterItem>(VisibleFilterItems[0]);
const selectedPhaseFilterItem = ref<FilterItem>(PhaseFilterItems[0]); const selectedPhaseFilterItem = ref<FilterItem>(PhaseFilterItems[0]);
const selectedCategoryFilterItem = ref<Category>();
const selectedTagFilterItem = ref<Tag>();
const selectedContributorItem = ref<User>();
function handleVisibleFilterItemChange(filterItem: FilterItem) { function handleVisibleFilterItemChange(filterItem: FilterItem) {
selectedVisibleFilterItem.value = filterItem; selectedVisibleFilterItem.value = filterItem;
@ -276,6 +306,21 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
selectedPhaseFilterItem.value = filterItem; selectedPhaseFilterItem.value = filterItem;
handleFetchPosts(); handleFetchPosts();
} }
function handleCategoryFilterItemChange(category?: Category) {
selectedCategoryFilterItem.value = category;
handleFetchPosts();
}
function handleTagFilterItemChange(tag?: Tag) {
selectedTagFilterItem.value = tag;
handleFetchPosts();
}
function handleContributorFilterItemChange(user?: User) {
selectedContributorItem.value = user;
handleFetchPosts();
}
</script> </script>
<template> <template>
<PostSettingModal <PostSettingModal
@ -325,9 +370,76 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
@change="handleCheckAllChange" @change="handleCheckAllChange"
/> />
</div> </div>
<div class="flex w-full flex-1 sm:w-auto"> <div class="flex w-full flex-1 items-center sm:w-auto">
<div v-if="!selectedPostNames.length"> <div
v-if="!selectedPostNames.length"
class="flex items-center gap-2"
>
<FormKit placeholder="输入关键词搜索" type="text"></FormKit> <FormKit placeholder="输入关键词搜索" type="text"></FormKit>
<div
v-if="selectedPhaseFilterItem.value"
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">
状态{{ selectedPhaseFilterItem.label }}
</span>
<IconCloseCircle
class="h-4 w-4 text-gray-600"
@click="handlePhaseFilterItemChange(PhaseFilterItems[0])"
/>
</div>
<div
v-if="selectedVisibleFilterItem.value"
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">
可见性{{ selectedVisibleFilterItem.label }}
</span>
<IconCloseCircle
class="h-4 w-4 text-gray-600"
@click="
handleVisibleFilterItemChange(VisibleFilterItems[0])
"
/>
</div>
<div
v-if="selectedCategoryFilterItem"
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">
分类{{ selectedCategoryFilterItem.spec.displayName }}
</span>
<IconCloseCircle
class="h-4 w-4 text-gray-600"
@click="handleCategoryFilterItemChange()"
/>
</div>
<div
v-if="selectedTagFilterItem"
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">
标签{{ selectedTagFilterItem.spec.displayName }}
</span>
<IconCloseCircle
class="h-4 w-4 text-gray-600"
@click="handleTagFilterItemChange()"
/>
</div>
<div
v-if="selectedContributorItem"
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">
标签{{ selectedContributorItem.spec.displayName }}
</span>
<IconCloseCircle
class="h-4 w-4 text-gray-600"
@click="handleContributorFilterItemChange()"
/>
</div>
</div> </div>
<VSpace v-else> <VSpace v-else>
<VButton type="default">设置</VButton> <VButton type="default">设置</VButton>
@ -423,9 +535,15 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
v-for="(category, index) in categories" v-for="(category, index) in categories"
:key="index" :key="index"
v-close-popper v-close-popper
@click="handleCategoryFilterItemChange(category)"
> >
<div <div
class="group relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50" class="group relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
:class="{
'bg-gray-100':
selectedCategoryFilterItem?.metadata.name ===
category.metadata.name,
}"
> >
<div class="relative flex flex-row items-center"> <div class="relative flex flex-row items-center">
<div class="flex-1"> <div class="flex-1">
@ -439,7 +557,7 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
</div> </div>
<div class="mt-1 flex"> <div class="mt-1 flex">
<span class="text-xs text-gray-500"> <span class="text-xs text-gray-500">
/categories/{{ category.spec.slug }} {{ category.status?.permalink }}
</span> </span>
</div> </div>
</div> </div>
@ -450,7 +568,8 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
<div <div
class="cursor-pointer text-sm text-gray-500 hover:text-gray-900" class="cursor-pointer text-sm text-gray-500 hover:text-gray-900"
> >
20 篇文章 {{ category.status?.posts?.length || 0 }}
篇文章
</div> </div>
</div> </div>
</div> </div>
@ -489,9 +608,15 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
v-for="(tag, index) in tags" v-for="(tag, index) in tags"
:key="index" :key="index"
v-close-popper v-close-popper
@click="handleTagFilterItemChange(tag)"
> >
<div <div
class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50" class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
:class="{
'bg-gray-100':
selectedTagFilterItem?.metadata.name ===
tag.metadata.name,
}"
> >
<div class="relative flex flex-row items-center"> <div class="relative flex flex-row items-center">
<div class="flex-1"> <div class="flex-1">
@ -500,7 +625,7 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
</div> </div>
<div class="mt-1 flex"> <div class="mt-1 flex">
<span class="text-xs text-gray-500"> <span class="text-xs text-gray-500">
/tags/{{ tag.spec.slug }} {{ tag.status?.permalink }}
</span> </span>
</div> </div>
</div> </div>
@ -511,7 +636,8 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
<div <div
class="cursor-pointer text-sm text-gray-500 hover:text-gray-900" class="cursor-pointer text-sm text-gray-500 hover:text-gray-900"
> >
20 篇文章 {{ tag.status?.posts?.length || 0 }}
篇文章
</div> </div>
</div> </div>
</div> </div>
@ -523,7 +649,10 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
</div> </div>
</template> </template>
</FloatingDropdown> </FloatingDropdown>
<UserDropdownSelector> <UserDropdownSelector
v-model:selected="selectedContributorItem"
@select="handleContributorFilterItemChange"
>
<div <div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black" class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
> >

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { VButton, VModal, VSpace, VTabItem, VTabs } from "@halo-dev/components"; import { VButton, VModal, VSpace, VTabItem, VTabs } from "@halo-dev/components";
import { computed, ref, watchEffect } from "vue"; import { computed, ref, watch, watchEffect } from "vue";
import type { PostRequest } from "@halo-dev/api-client"; import type { PostRequest } from "@halo-dev/api-client";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag"; import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
@ -67,7 +67,7 @@ const saving = ref(false);
const publishing = ref(false); const publishing = ref(false);
const publishCanceling = ref(false); const publishCanceling = ref(false);
const { categories } = usePostCategory({ fetchOnMounted: true }); const { categories, handleFetchCategories } = usePostCategory();
const categoriesMap = computed(() => { const categoriesMap = computed(() => {
return categories.value.map((category) => { return categories.value.map((category) => {
return { return {
@ -77,7 +77,7 @@ const categoriesMap = computed(() => {
}); });
}); });
const { tags } = usePostTag({ fetchOnMounted: true }); const { tags, handleFetchTags } = usePostTag();
const tagsMap = computed(() => { const tagsMap = computed(() => {
return tags.value.map((tag) => { return tags.value.map((tag) => {
return { return {
@ -180,6 +180,16 @@ const handlePublishCanceling = async () => {
} }
}; };
watch(
() => props.visible,
(visible) => {
if (visible) {
handleFetchCategories();
handleFetchTags();
}
}
);
watchEffect(() => { watchEffect(() => {
if (props.post) { if (props.post) {
formState.value = cloneDeep(props.post); formState.value = cloneDeep(props.post);