mirror of https://github.com/halo-dev/halo
feat: add support for selecting the parent category when creating a new category (#4056)
#### What type of PR is this? /area console /kind feature /milestone 2.7.x #### What this PR does / why we need it: 支持在创建分类的时候选择上级分类。 <img width="805" alt="image" src="https://github.com/halo-dev/halo/assets/21301288/02912f70-4de4-4b8e-bbbe-f973fbd3e684"> #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/4040 #### Special notes for your reviewer: 测试方式: 1. 测试新建的时候指定上级分类的功能是否正常。 2. 点击某个分类的操作按钮,选择添加子分类的菜单项,检查在新建分类的表单中上级分类是否选中了此分类。 #### Does this PR introduce a user-facing change? ```release-note Console 端的文章分类支持在新建时指定上级分类。 ```pull/4092/head^2
parent
4a1fe8dd1e
commit
2fd9cbde33
|
@ -276,6 +276,8 @@ core:
|
|||
delete:
|
||||
title: Are you sure you want to delete this category?
|
||||
description: After deleting this category, the association with corresponding articles will be removed. This operation cannot be undone.
|
||||
add_sub_category:
|
||||
button: Add sub category
|
||||
editing_modal:
|
||||
titles:
|
||||
update: Update post category
|
||||
|
@ -284,6 +286,8 @@ core:
|
|||
general: General
|
||||
annotations: Annotations
|
||||
fields:
|
||||
parent:
|
||||
label: Parent
|
||||
display_name:
|
||||
label: Display name
|
||||
slug:
|
||||
|
|
|
@ -276,6 +276,8 @@ core:
|
|||
delete:
|
||||
title: 确定要删除该分类吗?
|
||||
description: 删除此分类之后,对应文章的关联将被解除。该操作不可恢复。
|
||||
add_sub_category:
|
||||
button: 新增子分类
|
||||
editing_modal:
|
||||
titles:
|
||||
update: 编辑文章分类
|
||||
|
@ -284,6 +286,8 @@ core:
|
|||
general: 常规
|
||||
annotations: 元数据
|
||||
fields:
|
||||
parent:
|
||||
label: 上级分类
|
||||
display_name:
|
||||
label: 名称
|
||||
slug:
|
||||
|
|
|
@ -276,6 +276,8 @@ core:
|
|||
delete:
|
||||
title: 確定要刪除該分類嗎?
|
||||
description: 刪除此分類之後,對應文章的關聯將被解除。該操作不可恢復。
|
||||
add_sub_category:
|
||||
button: 新增子分類
|
||||
editing_modal:
|
||||
titles:
|
||||
update: 編輯文章分類
|
||||
|
@ -284,6 +286,8 @@ core:
|
|||
general: 常規
|
||||
annotations: 元數據
|
||||
fields:
|
||||
parent:
|
||||
label: 上級分類
|
||||
display_name:
|
||||
label: 名稱
|
||||
slug:
|
||||
|
|
|
@ -33,7 +33,8 @@ import { useDebounceFn } from "@vueuse/core";
|
|||
import { usePostCategory } from "./composables/use-post-category";
|
||||
|
||||
const editingModal = ref(false);
|
||||
const selectedCategory = ref<Category | null>(null);
|
||||
const selectedCategory = ref<Category>();
|
||||
const selectedParentCategory = ref<Category>();
|
||||
|
||||
const {
|
||||
categories,
|
||||
|
@ -68,8 +69,14 @@ const handleOpenEditingModal = (category: CategoryTree) => {
|
|||
editingModal.value = true;
|
||||
};
|
||||
|
||||
const handleOpenCreateByParentModal = (category: CategoryTree) => {
|
||||
selectedParentCategory.value = convertCategoryTreeToCategory(category);
|
||||
editingModal.value = true;
|
||||
};
|
||||
|
||||
const onEditingModalClose = () => {
|
||||
selectedCategory.value = null;
|
||||
selectedCategory.value = undefined;
|
||||
selectedParentCategory.value = undefined;
|
||||
handleFetchCategories();
|
||||
};
|
||||
</script>
|
||||
|
@ -77,6 +84,7 @@ const onEditingModalClose = () => {
|
|||
<CategoryEditingModal
|
||||
v-model:visible="editingModal"
|
||||
:category="selectedCategory"
|
||||
:parent-category="selectedParentCategory"
|
||||
@close="onEditingModalClose"
|
||||
/>
|
||||
<VPageHeader :title="$t('core.post_category.title')">
|
||||
|
@ -147,6 +155,7 @@ const onEditingModalClose = () => {
|
|||
@change="handleUpdateInBatch"
|
||||
@delete="handleDelete"
|
||||
@open-editing="handleOpenEditingModal"
|
||||
@open-create-by-parent="handleOpenCreateByParentModal"
|
||||
/>
|
||||
</Transition>
|
||||
</VCard>
|
||||
|
|
|
@ -28,11 +28,13 @@ import { useI18n } from "vue-i18n";
|
|||
const props = withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean;
|
||||
category: Category | null;
|
||||
category?: Category;
|
||||
parentCategory?: Category;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
category: null,
|
||||
category: undefined,
|
||||
parentCategory: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -63,6 +65,7 @@ const initialFormState: Category = {
|
|||
};
|
||||
|
||||
const formState = ref<Category>(cloneDeep(initialFormState));
|
||||
const selectedParentCategory = ref("");
|
||||
const saving = ref(false);
|
||||
|
||||
const isUpdateMode = computed(() => {
|
||||
|
@ -100,9 +103,45 @@ const handleSaveCategory = async () => {
|
|||
category: formState.value,
|
||||
});
|
||||
} else {
|
||||
await apiClient.extension.category.createcontentHaloRunV1alpha1Category({
|
||||
category: formState.value,
|
||||
});
|
||||
// Gets parent category, calculates priority and updates it.
|
||||
let parentCategory: Category | undefined = undefined;
|
||||
|
||||
if (selectedParentCategory.value) {
|
||||
const { data } =
|
||||
await apiClient.extension.category.getcontentHaloRunV1alpha1Category({
|
||||
name: selectedParentCategory.value,
|
||||
});
|
||||
parentCategory = data;
|
||||
}
|
||||
|
||||
const priority = parentCategory?.spec.children
|
||||
? parentCategory.spec.children.length + 1
|
||||
: 0;
|
||||
|
||||
formState.value.spec.priority = priority;
|
||||
|
||||
const { data: createdCategory } =
|
||||
await apiClient.extension.category.createcontentHaloRunV1alpha1Category(
|
||||
{
|
||||
category: formState.value,
|
||||
}
|
||||
);
|
||||
|
||||
if (parentCategory) {
|
||||
parentCategory.spec.children = Array.from(
|
||||
new Set([
|
||||
...(parentCategory.spec.children || []),
|
||||
createdCategory.metadata.name,
|
||||
])
|
||||
);
|
||||
|
||||
await apiClient.extension.category.updatecontentHaloRunV1alpha1Category(
|
||||
{
|
||||
name: selectedParentCategory.value,
|
||||
category: parentCategory,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
onVisibleChange(false);
|
||||
|
||||
|
@ -122,6 +161,7 @@ const onVisibleChange = (visible: boolean) => {
|
|||
};
|
||||
|
||||
const handleResetForm = () => {
|
||||
selectedParentCategory.value = "";
|
||||
formState.value = cloneDeep(initialFormState);
|
||||
reset("category-form");
|
||||
};
|
||||
|
@ -130,18 +170,15 @@ watch(
|
|||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
setFocus("displayNameInput");
|
||||
} else {
|
||||
handleResetForm();
|
||||
}
|
||||
}
|
||||
);
|
||||
if (props.parentCategory) {
|
||||
selectedParentCategory.value = props.parentCategory.metadata.name;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.category,
|
||||
(category) => {
|
||||
if (category) {
|
||||
formState.value = cloneDeep(category);
|
||||
if (props.category) {
|
||||
formState.value = cloneDeep(props.category);
|
||||
}
|
||||
|
||||
setFocus("displayNameInput");
|
||||
} else {
|
||||
handleResetForm();
|
||||
}
|
||||
|
@ -189,6 +226,14 @@ const { handleGenerateSlug } = useSlugify(
|
|||
</div>
|
||||
</div>
|
||||
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||
<FormKit
|
||||
v-if="!isUpdateMode"
|
||||
v-model="selectedParentCategory"
|
||||
type="categorySelect"
|
||||
:label="
|
||||
$t('core.post_category.editing_modal.fields.parent.label')
|
||||
"
|
||||
></FormKit>
|
||||
<FormKit
|
||||
id="displayNameInput"
|
||||
v-model="formState.spec.displayName"
|
||||
|
|
|
@ -26,6 +26,7 @@ withDefaults(
|
|||
const emit = defineEmits<{
|
||||
(event: "change"): void;
|
||||
(event: "open-editing", category: CategoryTree): void;
|
||||
(event: "open-create-by-parent", category: CategoryTree): void;
|
||||
(event: "delete", category: CategoryTree): void;
|
||||
}>();
|
||||
|
||||
|
@ -39,6 +40,10 @@ function onOpenEditingModal(category: CategoryTree) {
|
|||
emit("open-editing", category);
|
||||
}
|
||||
|
||||
function onOpenCreateByParentModal(category: CategoryTree) {
|
||||
emit("open-create-by-parent", category);
|
||||
}
|
||||
|
||||
function onDelete(category: CategoryTree) {
|
||||
emit("delete", category);
|
||||
}
|
||||
|
@ -117,6 +122,9 @@ function onDelete(category: CategoryTree) {
|
|||
>
|
||||
{{ $t("core.common.buttons.edit") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem @click="onOpenCreateByParentModal(category)">
|
||||
{{ $t("core.post_category.operations.add_sub_category.button") }}
|
||||
</VDropdownItem>
|
||||
<VDropdownItem
|
||||
v-permission="['system:posts:manage']"
|
||||
type="danger"
|
||||
|
@ -132,6 +140,7 @@ function onDelete(category: CategoryTree) {
|
|||
@change="onChange"
|
||||
@delete="onDelete"
|
||||
@open-editing="onOpenEditingModal"
|
||||
@open-create-by-parent="onOpenCreateByParentModal"
|
||||
/>
|
||||
</li>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue