feat: record the single page query conditions in the route query parameters (#4208)

#### What type of PR is this?

/area console
/kind feature
/milestone 2.8.x

#### What this PR does / why we need it:

在页面数据管理列表页面路由中记录查询条件,包括分页信息、筛选信息等。可以保证在刷新浏览器窗口或者从编辑页面返回时保留之前的查询状态。

<img width="1597" alt="image" src="https://github.com/halo-dev/halo/assets/21301288/5e14019b-9751-4a3f-b5c4-ec28b8ba4380">

#### Special notes for your reviewer:

需要测试:

1. 页面管理列表的所有筛选项是否可以正常工作。
2. 尝试设置部分筛选,然后刷新页面,观察筛选条件是否正常保留。
3. 尝试设置部分筛选,然后选择任意一个页面进行编辑,观察发布之后是否正常返回到管理列表,并正确设置了查询参数。

#### Does this PR introduce a user-facing change?

```release-note
Console 端的页面管理列表支持在地址栏记录筛选条件。
```
pull/4220/head^2
Ryan Wang 2023-07-19 19:46:17 +08:00 committed by GitHub
parent 01b81f1afc
commit 2ade57184c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 25 additions and 19 deletions

View File

@ -26,7 +26,7 @@ const emit = defineEmits<{
(event: "update:modelValue", value?: string): void; (event: "update:modelValue", value?: string): void;
}>(); }>();
const { users, handleFetchUsers } = useUserFetch(); const { users } = useUserFetch({ fetchOnMounted: true });
const dropdown = ref(); const dropdown = ref();
@ -41,7 +41,6 @@ const handleSelect = (user: User) => {
}; };
function onDropdownShow() { function onDropdownShow() {
handleFetchUsers();
setTimeout(() => { setTimeout(() => {
setFocus("userFilterDropdownInput"); setFocus("userFilterDropdownInput");
}, 200); }, 200);

View File

@ -188,7 +188,7 @@ const handlePublish = async () => {
if (returnToView.value && permalink) { if (returnToView.value && permalink) {
window.location.href = permalink; window.location.href = permalink;
} else { } else {
router.push({ name: "SinglePages" }); router.back();
} }
} else { } else {
formState.value.page.spec.publish = true; formState.value.page.spec.publish = true;

View File

@ -36,6 +36,7 @@ import { singlePageLabels } from "@/constants/labels";
import { useMutation, useQuery } from "@tanstack/vue-query"; import { useMutation, useQuery } from "@tanstack/vue-query";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import UserFilterDropdown from "@/components/filter/UserFilterDropdown.vue"; import UserFilterDropdown from "@/components/filter/UserFilterDropdown.vue";
import { useRouteQuery } from "@vueuse/router";
const { currentUserHasPermission } = usePermission(); const { currentUserHasPermission } = usePermission();
const { t } = useI18n(); const { t } = useI18n();
@ -46,18 +47,20 @@ const selectedPageNames = ref<string[]>([]);
const checkedAll = ref(false); const checkedAll = ref(false);
// Filters // Filters
const selectedContributor = ref(); const selectedContributor = useRouteQuery<string | undefined>("contributor");
const selectedVisible = ref(); const selectedVisible = useRouteQuery<
const selectedPublishStatus = ref(); "PUBLIC" | "INTERNAL" | "PRIVATE" | undefined
const selectedSortValue = ref(); >("visible");
const keyword = ref(""); const selectedPublishStatus = useRouteQuery<string | undefined>("status");
const selectedSort = useRouteQuery<string | undefined>("sort");
const keyword = useRouteQuery<string>("keyword", "");
watch( watch(
() => [ () => [
selectedContributor.value, selectedContributor.value,
selectedVisible.value, selectedVisible.value,
selectedPublishStatus.value, selectedPublishStatus.value,
selectedSortValue.value, selectedSort.value,
keyword.value, keyword.value,
], ],
() => { () => {
@ -70,7 +73,7 @@ const hasFilters = computed(() => {
selectedContributor.value || selectedContributor.value ||
selectedVisible.value || selectedVisible.value ||
selectedPublishStatus.value !== undefined || selectedPublishStatus.value !== undefined ||
selectedSortValue.value selectedSort.value
); );
}); });
@ -78,11 +81,15 @@ function handleClearFilters() {
selectedContributor.value = undefined; selectedContributor.value = undefined;
selectedVisible.value = undefined; selectedVisible.value = undefined;
selectedPublishStatus.value = undefined; selectedPublishStatus.value = undefined;
selectedSortValue.value = undefined; selectedSort.value = undefined;
} }
const page = ref(1); const page = useRouteQuery<number>("page", 1, {
const size = ref(20); transform: Number,
});
const size = useRouteQuery<number>("size", 20, {
transform: Number,
});
const total = ref(0); const total = ref(0);
const hasNext = ref(false); const hasNext = ref(false);
const hasPrevious = ref(false); const hasPrevious = ref(false);
@ -100,7 +107,7 @@ const {
page, page,
size, size,
selectedVisible, selectedVisible,
selectedSortValue, selectedSort,
keyword, keyword,
], ],
queryFn: async () => { queryFn: async () => {
@ -122,7 +129,7 @@ const {
page: page.value, page: page.value,
size: size.value, size: size.value,
visible: selectedVisible.value, visible: selectedVisible.value,
sort: [selectedSortValue.value].filter(Boolean) as string[], sort: [selectedSort.value].filter(Boolean) as string[],
keyword: keyword.value, keyword: keyword.value,
contributor: contributors, contributor: contributors,
}); });
@ -429,11 +436,11 @@ const getExternalUrl = (singlePage: SinglePage) => {
}, },
{ {
label: t('core.page.filters.status.items.published'), label: t('core.page.filters.status.items.published'),
value: true, value: 'true',
}, },
{ {
label: t('core.page.filters.status.items.draft'), label: t('core.page.filters.status.items.draft'),
value: false, value: 'false',
}, },
]" ]"
/> />
@ -460,7 +467,7 @@ const getExternalUrl = (singlePage: SinglePage) => {
:label="$t('core.page.filters.author.label')" :label="$t('core.page.filters.author.label')"
/> />
<FilterDropdown <FilterDropdown
v-model="selectedSortValue" v-model="selectedSort"
:label="$t('core.common.filters.labels.sort')" :label="$t('core.common.filters.labels.sort')"
:items="[ :items="[
{ {

View File

@ -190,7 +190,7 @@ const handlePublish = async () => {
if (returnToView.value === "true" && permalink) { if (returnToView.value === "true" && permalink) {
window.location.href = permalink; window.location.href = permalink;
} else { } else {
router.push({ name: "Posts" }); router.back();
} }
} else { } else {
const { data } = await apiClient.post.draftPost({ const { data } = await apiClient.post.draftPost({