Skip to content

Commit

Permalink
feat: add batch setting for partial post fields (#6142)
Browse files Browse the repository at this point in the history
#### What type of PR is this?

/kind feature
/area ui
/milestone 2.17.x

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

支持批量为文章设置部分属性。

![image](https://github.com/halo-dev/halo/assets/21301288/cc4aa912-20ba-4b50-869b-705702f56d7d)

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

Fixes #4631

#### Special notes for your reviewer:

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

```release-note
支持批量为文章设置部分属性。
```
  • Loading branch information
ruibaby authored Jun 26, 2024
1 parent 5d5df7c commit 2ae5d22
Show file tree
Hide file tree
Showing 5 changed files with 396 additions and 0 deletions.
26 changes: 26 additions & 0 deletions ui/console-src/modules/contents/posts/PostList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import UserFilterDropdown from "@/components/filter/UserFilterDropdown.vue";
import CategoryFilterDropdown from "@/components/filter/CategoryFilterDropdown.vue";
import TagFilterDropdown from "@/components/filter/TagFilterDropdown.vue";
import PostListItem from "./components/PostListItem.vue";
import PostBatchSettingModal from "./components/PostBatchSettingModal.vue";
const { t } = useI18n();
Expand Down Expand Up @@ -320,6 +321,23 @@ const handleCancelPublishInBatch = async () => {
});
};
// Batch settings
const batchSettingModalVisible = ref(false);
const batchSettingPosts = ref<ListedPost[]>([]);
function handleOpenBatchSettingModal() {
batchSettingPosts.value = selectedPostNames.value.map((name) => {
return posts.value?.find((post) => post.post.metadata.name === name);
}) as ListedPost[];
batchSettingModalVisible.value = true;
}
function onBatchSettingModalClose() {
batchSettingModalVisible.value = false;
batchSettingPosts.value = [];
}
watch(
() => selectedPostNames.value,
(newValue) => {
Expand All @@ -342,6 +360,11 @@ watch(
</span>
</template>
</PostSettingModal>
<PostBatchSettingModal
v-if="batchSettingModalVisible"
:posts="batchSettingPosts"
@close="onBatchSettingModalClose"
/>
<VPageHeader :title="$t('core.post.title')">
<template #icon>
<IconBookRead class="mr-2 self-center" />
Expand Down Expand Up @@ -398,6 +421,9 @@ watch(
<VButton @click="handleCancelPublishInBatch">
{{ $t("core.common.buttons.cancel_publish") }}
</VButton>
<VButton @click="handleOpenBatchSettingModal">
{{ $t("core.post.operations.batch_setting.button") }}
</VButton>
<VButton type="danger" @click="handleDeleteInBatch">
{{ $t("core.common.buttons.delete") }}
</VButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
<script lang="ts" setup>
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
import {
coreApiClient,
type JsonPatchInner,
type ListedPost,
} from "@halo-dev/api-client";
import { ref } from "vue";
import { useMutation, useQueryClient } from "@tanstack/vue-query";
import { useI18n } from "vue-i18n";
type ArrayPatchOp = "add" | "replace" | "removeAll";
interface FormData {
category: {
enabled: boolean;
names?: string[];
op: ArrayPatchOp;
};
tag: {
enabled: boolean;
names?: string[];
op: ArrayPatchOp;
};
visible: {
enabled: boolean;
value: "PUBLIC" | "PRIVATE";
};
allowComment: {
enabled: boolean;
value: boolean;
};
}
const { t } = useI18n();
const queryClient = useQueryClient();
const props = withDefaults(defineProps<{ posts: ListedPost[] }>(), {});
const emit = defineEmits<{
(event: "close"): void;
}>();
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 jsonPatchInner: JsonPatchInner[] = [];
if (data.category.enabled) {
jsonPatchInner.push({
op: "add",
path: "/spec/categories",
value: computeArrayPatchValue(
data.category.op,
post.post.spec.categories || [],
data.category.names || []
),
});
}
if (data.tag.enabled) {
jsonPatchInner.push({
op: "add",
path: "/spec/tags",
value: computeArrayPatchValue(
data.tag.op,
post.post.spec.tags || [],
data.tag.names || []
),
});
}
if (data.visible.enabled) {
jsonPatchInner.push({
op: "add",
path: "/spec/visible",
value: data.visible.value,
});
}
if (data.allowComment.enabled) {
jsonPatchInner.push({
op: "add",
path: "/spec/allowComment",
value: data.allowComment.value,
});
}
await coreApiClient.content.post.patchPost({
name: post.post.metadata.name,
jsonPatchInner,
});
}
Toast.success(t("core.common.toast.save_success"));
},
onSuccess() {
queryClient.invalidateQueries({ queryKey: ["posts"] });
modal.value?.close();
},
onError() {
Toast.error(t("core.common.toast.save_failed_and_retry"));
},
});
function computeArrayPatchValue(
op: ArrayPatchOp,
oldValue: string[],
newValue: string[]
) {
if (op === "add") {
return Array.from(new Set([...oldValue, ...newValue]));
} else if (op === "replace") {
return newValue;
} else if (op === "removeAll") {
return [];
}
}
function onSubmit(data: FormData) {
mutate({ data });
}
</script>

<template>
<VModal
ref="modal"
height="calc(100vh - 20px)"
:title="$t('core.post.batch_setting_modal.title')"
:width="700"
@close="emit('close')"
>
<FormKit
id="post-batch-settings-form"
type="form"
name="post-batch-settings-form"
@submit="onSubmit"
>
<FormKit
v-slot="{ value }"
name="category"
type="group"
:label="$t('core.post.batch_setting_modal.fields.category_group')"
>
<FormKit
:value="false"
:label="$t('core.post.batch_setting_modal.fields.common.enabled')"
type="checkbox"
name="enabled"
></FormKit>
<FormKit
v-if="value?.enabled"
type="select"
:options="[
{
value: 'add',
label: $t(
'core.post.batch_setting_modal.fields.common.op.options.add'
),
},
{
value: 'replace',
label: $t(
'core.post.batch_setting_modal.fields.common.op.options.replace'
),
},
{
value: 'removeAll',
label: $t(
'core.post.batch_setting_modal.fields.common.op.options.remove_all'
),
},
]"
:label="$t('core.post.batch_setting_modal.fields.common.op.label')"
name="op"
value="add"
></FormKit>
<FormKit
v-if="value?.enabled && value?.op !== 'removeAll'"
:label="$t('core.post.batch_setting_modal.fields.category_names')"
type="categorySelect"
:multiple="true"
name="names"
validation="required"
></FormKit>
</FormKit>
<FormKit
v-slot="{ value }"
type="group"
name="tag"
:label="$t('core.post.batch_setting_modal.fields.tag_group')"
>
<FormKit
:value="false"
:label="$t('core.post.batch_setting_modal.fields.common.enabled')"
type="checkbox"
name="enabled"
></FormKit>
<FormKit
v-if="value?.enabled"
type="select"
:options="[
{
value: 'add',
label: $t(
'core.post.batch_setting_modal.fields.common.op.options.add'
),
},
{
value: 'replace',
label: $t(
'core.post.batch_setting_modal.fields.common.op.options.replace'
),
},
{
value: 'removeAll',
label: $t(
'core.post.batch_setting_modal.fields.common.op.options.remove_all'
),
},
]"
:label="$t('core.post.batch_setting_modal.fields.common.op.label')"
name="op"
value="add"
></FormKit>
<FormKit
v-if="value?.enabled && value?.op !== 'removeAll'"
:label="$t('core.post.batch_setting_modal.fields.tag_names')"
type="tagSelect"
:multiple="true"
name="names"
validation="required"
></FormKit>
</FormKit>
<FormKit
v-slot="{ value }"
type="group"
name="visible"
:label="$t('core.post.batch_setting_modal.fields.visible_group')"
>
<FormKit
:value="false"
:label="$t('core.post.batch_setting_modal.fields.common.enabled')"
type="checkbox"
name="enabled"
></FormKit>
<FormKit
v-if="value?.enabled"
:options="[
{ label: $t('core.common.select.public'), value: 'PUBLIC' },
{
label: $t('core.common.select.private'),
value: 'PRIVATE',
},
]"
:label="$t('core.post.batch_setting_modal.fields.visible_value')"
name="value"
type="select"
value="PUBLIC"
></FormKit>
</FormKit>
<FormKit
v-slot="{ value }"
type="group"
name="allowComment"
:label="$t('core.post.batch_setting_modal.fields.allow_comment_group')"
>
<FormKit
:value="false"
:label="$t('core.post.batch_setting_modal.fields.common.enabled')"
type="checkbox"
name="enabled"
></FormKit>
<FormKit
v-if="value?.enabled"
:options="[
{ label: $t('core.common.radio.yes'), value: true },
{ label: $t('core.common.radio.no'), value: false },
]"
:label="
$t('core.post.batch_setting_modal.fields.allow_comment_value')
"
name="value"
type="radio"
:value="true"
></FormKit>
</FormKit>
</FormKit>
<template #footer>
<VSpace>
<VButton
type="secondary"
:loading="isLoading"
@click="$formkit.submit('post-batch-settings-form')"
>
{{ $t("core.common.buttons.save") }}
</VButton>
<VButton @click="modal?.close()">
{{ $t("core.common.buttons.cancel") }}
</VButton>
</VSpace>
</template>
</VModal>
</template>
21 changes: 21 additions & 0 deletions ui/src/locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ core:
description: >-
Batch cancel publish posts, the selected posts will be set to
unpublished status
batch_setting:
button: Batch settings
filters:
status:
items:
Expand Down Expand Up @@ -285,6 +287,25 @@ core:
create_time_asc: Earliest Created
display_name_desc: Descending order by tag name
display_name_asc: Ascending order by tag name
batch_setting_modal:
title: Post batch settings
fields:
common:
enabled: Enabled
op:
label: Operate
options:
add: Add
replace: Replace
remove_all: Remove all
category_group: Category
category_names: Select categories
tag_group: Tag
tag_names: Select tags
visible_group: Visible
visible_value: "Select visible option "
allow_comment_group: " Allow comment"
allow_comment_value: Choose whether to allow comments
deleted_post:
title: Deleted Posts
empty:
Expand Down
Loading

0 comments on commit 2ae5d22

Please sign in to comment.