mirror of https://github.com/halo-dev/halo
feat: add batch setting for partial post fields (#6142)
#### What type of PR is this? /kind feature /area ui /milestone 2.17.x #### What this PR does / why we need it: 支持批量为文章设置部分属性。  #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/4631 #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note 支持批量为文章设置部分属性。 ```pull/6103/head
parent
5d5df7c7a9
commit
2ae5d222d9
|
@ -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();
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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:
|
||||
|
@ -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:
|
||||
|
|
|
@ -191,6 +191,8 @@ core:
|
|||
cancel_publish_in_batch:
|
||||
title: 取消发布文章
|
||||
description: 批量取消发布文章,所选文章会被设置为未发布状态
|
||||
batch_setting:
|
||||
button: 批量设置
|
||||
filters:
|
||||
status:
|
||||
items:
|
||||
|
@ -273,6 +275,25 @@ core:
|
|||
create_time_asc: 较早创建
|
||||
display_name_desc: 标签名降序
|
||||
display_name_asc: 标签名升序
|
||||
batch_setting_modal:
|
||||
title: 文章批量设置
|
||||
fields:
|
||||
common:
|
||||
enabled: 启用
|
||||
op:
|
||||
label: 设置方式
|
||||
options:
|
||||
add: 追加
|
||||
replace: 替换
|
||||
remove_all: 移除全部
|
||||
category_group: 分类
|
||||
category_names: 选择分类
|
||||
tag_group: 标签
|
||||
tag_names: 选择标签
|
||||
visible_group: 可见性
|
||||
visible_value: 选择可见性
|
||||
allow_comment_group: 允许评论
|
||||
allow_comment_value: 选择是否允许评论
|
||||
deleted_post:
|
||||
title: 文章回收站
|
||||
empty:
|
||||
|
|
|
@ -191,6 +191,8 @@ core:
|
|||
cancel_publish_in_batch:
|
||||
title: 取消發佈文章
|
||||
description: 批量取消發佈文章,所選文章會被設置為未發布狀態
|
||||
batch_setting:
|
||||
button: 批量設置
|
||||
filters:
|
||||
status:
|
||||
items:
|
||||
|
@ -265,6 +267,25 @@ core:
|
|||
label: 自定義模板
|
||||
cover:
|
||||
label: 封面圖
|
||||
batch_setting_modal:
|
||||
title: 文章批量設置
|
||||
fields:
|
||||
common:
|
||||
enabled: 啟用
|
||||
op:
|
||||
label: 設置方式
|
||||
options:
|
||||
add: 追加
|
||||
replace: 替换
|
||||
remove_all: 移除全部
|
||||
category_group: 分类
|
||||
category_names: 選擇分類
|
||||
tag_group: 標籤
|
||||
tag_names: 選擇標籤
|
||||
visible_group: 可見性
|
||||
visible_value: 選擇可見性
|
||||
allow_comment_group: 允許評論
|
||||
allow_comment_value: 選擇是否允許評論
|
||||
deleted_post:
|
||||
title: 文章回收站
|
||||
empty:
|
||||
|
|
Loading…
Reference in New Issue