refactor: add chunked execution mechanism for post batch operations (#7378)

#### What type of PR is this?

/area ui
/kind improvement
/milestone 2.20.x

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

Use `Promise.all` to execute part of the batch operation logic of the post in chunks to optimize the execution performance.

#### Which issue(s) this PR fixes:

Fixes #

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

```release-note
优化文章部分批量操作的执行性能
```
pull/7386/head
Ryan Wang 2025-04-23 22:17:13 +08:00 committed by GitHub
parent c2819f1f5a
commit 7a315302a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 123 additions and 90 deletions

View File

@ -23,6 +23,7 @@ import {
VStatusDot,
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { chunk } from "lodash-es";
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import PostTag from "./tags/components/PostTag.vue";
@ -109,13 +110,18 @@ const handleDeletePermanentlyInBatch = async () => {
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
onConfirm: async () => {
const chunks = chunk(selectedPostNames.value, 5);
for (const chunk of chunks) {
await Promise.all(
selectedPostNames.value.map((name) => {
chunk.map((name) => {
return coreApiClient.content.post.deletePost({
name,
});
})
);
}
await refetch();
selectedPostNames.value = [];
@ -158,8 +164,11 @@ const handleRecoveryInBatch = async () => {
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
onConfirm: async () => {
const chunks = chunk(selectedPostNames.value, 5);
for (const chunk of chunks) {
await Promise.all(
selectedPostNames.value.map((name) => {
chunk.map((name) => {
const isPostExist = posts.value?.some(
(item) => item.post.metadata.name === name
);
@ -180,6 +189,8 @@ const handleRecoveryInBatch = async () => {
});
})
);
}
await refetch();
selectedPostNames.value = [];

View File

@ -23,6 +23,7 @@ import {
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { useRouteQuery } from "@vueuse/router";
import { chunk } from "lodash-es";
import type { Ref } from "vue";
import { computed, provide, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
@ -266,13 +267,18 @@ const handleDeleteInBatch = async () => {
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
onConfirm: async () => {
const chunks = chunk(selectedPostNames.value, 5);
for (const chunk of chunks) {
await Promise.all(
selectedPostNames.value.map((name) => {
chunk.map((name) => {
return consoleApiClient.content.post.recyclePost({
name,
});
})
);
}
await refetch();
selectedPostNames.value = [];
@ -288,9 +294,14 @@ const handlePublishInBatch = async () => {
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
onConfirm: async () => {
for (const i in selectedPostNames.value) {
const name = selectedPostNames.value[i];
await consoleApiClient.content.post.publishPost({ name });
const chunks = chunk(selectedPostNames.value, 5);
for (const chunk of chunks) {
await Promise.all(
chunk.map((name) => {
return consoleApiClient.content.post.publishPost({ name });
})
);
}
await refetch();
@ -308,9 +319,14 @@ const handleCancelPublishInBatch = async () => {
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
onConfirm: async () => {
for (const i in selectedPostNames.value) {
const name = selectedPostNames.value[i];
await consoleApiClient.content.post.unpublishPost({ name });
const chunks = chunk(selectedPostNames.value, 5);
for (const chunk of chunks) {
await Promise.all(
chunk.map((name) => {
return consoleApiClient.content.post.unpublishPost({ name });
})
);
}
await refetch();

View File

@ -6,6 +6,7 @@ import {
} from "@halo-dev/api-client";
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { chunk } from "lodash-es";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
@ -50,8 +51,11 @@ const modal = ref<InstanceType<typeof VModal> | null>(null);
const { mutate, isLoading } = useMutation({
mutationKey: ["batch-update-posts"],
mutationFn: async ({ data }: { data: FormData }) => {
for (const key in props.posts) {
const post = props.posts[key];
const postChunks = chunk(props.posts, 5);
for (const postChunk of postChunks) {
await Promise.all(
postChunk.map((post) => {
const jsonPatchInner: JsonPatchInner[] = [];
if (data.category.enabled) {
jsonPatchInner.push({
@ -101,10 +105,12 @@ const { mutate, isLoading } = useMutation({
});
}
await coreApiClient.content.post.patchPost({
return coreApiClient.content.post.patchPost({
name: post.post.metadata.name,
jsonPatchInner,
});
})
);
}
Toast.success(t("core.common.toast.save_success"));