diff --git a/api-docs/openapi/v3_0/aggregated.json b/api-docs/openapi/v3_0/aggregated.json index f6621399c..80db1e8f7 100644 --- a/api-docs/openapi/v3_0/aggregated.json +++ b/api-docs/openapi/v3_0/aggregated.json @@ -15506,6 +15506,9 @@ "minLength": 1, "type": "string" }, + "hideFromList": { + "type": "boolean" + }, "postTemplate": { "maxLength": 255, "type": "string" @@ -19659,6 +19662,9 @@ "excerpt": { "type": "string" }, + "hideFromList": { + "type": "boolean" + }, "inProgress": { "type": "boolean" }, @@ -21468,6 +21474,9 @@ "excerpt": { "type": "string" }, + "hideFromList": { + "type": "boolean" + }, "inProgress": { "type": "boolean" }, diff --git a/api/src/main/java/run/halo/app/core/extension/content/Category.java b/api/src/main/java/run/halo/app/core/extension/content/Category.java index a2ddb04b1..245f681b4 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/Category.java +++ b/api/src/main/java/run/halo/app/core/extension/content/Category.java @@ -27,6 +27,7 @@ import run.halo.app.extension.GroupVersionKind; public class Category extends AbstractExtension { public static final String KIND = "Category"; + public static final String LAST_HIDDEN_STATE_ANNO = "content.halo.run/last-hidden-state"; public static final GroupVersionKind GVK = GroupVersionKind.fromExtension(Category.class); @@ -79,6 +80,15 @@ public class Category extends AbstractExtension { * and B will be queried, but C and D will not be queried.

*/ private boolean preventParentPostCascadeQuery; + + /** + *

Whether to hide the category from the category list.

+ *

When set to true, the category including its subcategories and related posts will + * not be displayed in the category list, but it can still be accessed by permalink.

+ *

Limitation: It only takes effect on the theme-side categorized list and it only + * allows to be set to true on the first level(root node) of categories.

+ */ + private boolean hideFromList; } @JsonIgnore diff --git a/api/src/main/java/run/halo/app/core/extension/content/Post.java b/api/src/main/java/run/halo/app/core/extension/content/Post.java index b9209688b..4ea16e74a 100644 --- a/api/src/main/java/run/halo/app/core/extension/content/Post.java +++ b/api/src/main/java/run/halo/app/core/extension/content/Post.java @@ -168,6 +168,11 @@ public class Post extends AbstractExtension { private List contributors; + /** + * see {@link Category.CategorySpec#isHideFromList()}. + */ + private Boolean hideFromList; + private Instant lastModifyTime; private Long observedVersion; diff --git a/api/src/main/java/run/halo/app/extension/index/query/NotEqual.java b/api/src/main/java/run/halo/app/extension/index/query/NotEqual.java index 3ffa33ff0..af8f694dd 100644 --- a/api/src/main/java/run/halo/app/extension/index/query/NotEqual.java +++ b/api/src/main/java/run/halo/app/extension/index/query/NotEqual.java @@ -21,7 +21,7 @@ public class NotEqual extends SimpleQuery { indexView.acquireReadLock(); try { NavigableSet equalNames = equalQuery.matches(indexView); - NavigableSet allNames = indexView.getIdsForField(fieldName); + NavigableSet allNames = indexView.getAllIds(); allNames.removeAll(equalNames); return allNames; } finally { diff --git a/application/src/main/java/run/halo/app/content/AbstractEventReconciler.java b/application/src/main/java/run/halo/app/content/AbstractEventReconciler.java index 28105aa78..1a3471921 100644 --- a/application/src/main/java/run/halo/app/content/AbstractEventReconciler.java +++ b/application/src/main/java/run/halo/app/content/AbstractEventReconciler.java @@ -3,7 +3,6 @@ package run.halo.app.content; import java.time.Duration; import java.time.Instant; import org.springframework.context.SmartLifecycle; -import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.controller.Controller; import run.halo.app.extension.controller.ControllerBuilder; import run.halo.app.extension.controller.DefaultController; @@ -24,12 +23,9 @@ public abstract class AbstractEventReconciler implements Reconciler, Smart protected volatile boolean running = false; - protected final ExtensionClient client; - private final String controllerName; - protected AbstractEventReconciler(String controllerName, ExtensionClient client) { - this.client = client; + protected AbstractEventReconciler(String controllerName) { this.controllerName = controllerName; this.queue = new DefaultQueue<>(Instant::now); this.controller = this.setupWith(null); diff --git a/application/src/main/java/run/halo/app/content/CategoryPostCountUpdater.java b/application/src/main/java/run/halo/app/content/CategoryPostCountUpdater.java index 3c87fc328..0c7c71435 100644 --- a/application/src/main/java/run/halo/app/content/CategoryPostCountUpdater.java +++ b/application/src/main/java/run/halo/app/content/CategoryPostCountUpdater.java @@ -37,10 +37,12 @@ import run.halo.app.infra.utils.JsonUtils; public class CategoryPostCountUpdater extends AbstractEventReconciler { + protected final ExtensionClient client; private final CategoryPostCountService categoryPostCountService; public CategoryPostCountUpdater(ExtensionClient client) { - super(CategoryPostCountUpdater.class.getName(), client); + super(CategoryPostCountUpdater.class.getName()); + this.client = client; this.categoryPostCountService = new CategoryPostCountService(client); } diff --git a/application/src/main/java/run/halo/app/content/CategoryService.java b/application/src/main/java/run/halo/app/content/CategoryService.java index 0b8a17ea1..36f14e5a5 100644 --- a/application/src/main/java/run/halo/app/content/CategoryService.java +++ b/application/src/main/java/run/halo/app/content/CategoryService.java @@ -2,9 +2,14 @@ package run.halo.app.content; import org.springframework.lang.NonNull; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import run.halo.app.core.extension.content.Category; public interface CategoryService { Flux listChildren(@NonNull String categoryName); + + Mono getParentByName(@NonNull String categoryName); + + Mono isCategoryHidden(@NonNull String categoryName); } diff --git a/application/src/main/java/run/halo/app/content/PostHideFromListStateUpdater.java b/application/src/main/java/run/halo/app/content/PostHideFromListStateUpdater.java new file mode 100644 index 000000000..12bd88fe8 --- /dev/null +++ b/application/src/main/java/run/halo/app/content/PostHideFromListStateUpdater.java @@ -0,0 +1,55 @@ +package run.halo.app.content; + +import static run.halo.app.extension.index.query.QueryFactory.equal; + +import org.springframework.context.event.EventListener; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import run.halo.app.core.extension.content.Post; +import run.halo.app.event.post.CategoryHiddenStateChangeEvent; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.extension.router.selector.FieldSelector; +import run.halo.app.infra.ReactiveExtensionPaginatedOperator; + +/** + * Synchronize the {@link Post.PostStatus#getHideFromList()} state of the post with the category. + * + * @author guqing + * @since 2.17.0 + */ +@Component +public class PostHideFromListStateUpdater + extends AbstractEventReconciler { + private final ReactiveExtensionPaginatedOperator reactiveExtensionPaginatedOperator; + private final ReactiveExtensionClient client; + + protected PostHideFromListStateUpdater(ReactiveExtensionClient client, + ReactiveExtensionPaginatedOperator reactiveExtensionPaginatedOperator) { + super(PostHideFromListStateUpdater.class.getName()); + this.reactiveExtensionPaginatedOperator = reactiveExtensionPaginatedOperator; + this.client = client; + } + + @Override + public Result reconcile(CategoryHiddenStateChangeEvent request) { + var listOptions = new ListOptions(); + listOptions.setFieldSelector(FieldSelector.of( + equal("spec.categories", request.getCategoryName()) + )); + + reactiveExtensionPaginatedOperator.list(Post.class, listOptions) + .flatMap(post -> { + post.getStatusOrDefault().setHideFromList(request.isHidden()); + return client.update(post); + }) + .then() + .block(); + return Result.doNotRetry(); + } + + @EventListener(CategoryHiddenStateChangeEvent.class) + public void onApplicationEvent(@NonNull CategoryHiddenStateChangeEvent event) { + this.queue.addImmediately(event); + } +} diff --git a/application/src/main/java/run/halo/app/content/TagPostCountUpdater.java b/application/src/main/java/run/halo/app/content/TagPostCountUpdater.java index a1e004367..481da4559 100644 --- a/application/src/main/java/run/halo/app/content/TagPostCountUpdater.java +++ b/application/src/main/java/run/halo/app/content/TagPostCountUpdater.java @@ -35,9 +35,11 @@ import run.halo.app.infra.utils.JsonUtils; @Component public class TagPostCountUpdater extends AbstractEventReconciler { + private final ExtensionClient client; public TagPostCountUpdater(ExtensionClient client) { - super(TagPostCountUpdater.class.getName(), client); + super(TagPostCountUpdater.class.getName()); + this.client = client; } @Override diff --git a/application/src/main/java/run/halo/app/content/impl/CategoryServiceImpl.java b/application/src/main/java/run/halo/app/content/impl/CategoryServiceImpl.java index 3ee561e61..b7328ab2a 100644 --- a/application/src/main/java/run/halo/app/content/impl/CategoryServiceImpl.java +++ b/application/src/main/java/run/halo/app/content/impl/CategoryServiceImpl.java @@ -1,13 +1,21 @@ package run.halo.app.content.impl; +import static run.halo.app.extension.index.query.QueryFactory.equal; + import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Sort; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import run.halo.app.content.CategoryService; import run.halo.app.core.extension.content.Category; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.ListResult; +import run.halo.app.extension.PageRequestImpl; import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.extension.router.selector.FieldSelector; @Component @RequiredArgsConstructor @@ -28,6 +36,35 @@ public class CategoryServiceImpl implements CategoryService { }); } + @Override + public Mono getParentByName(@NonNull String name) { + if (StringUtils.isBlank(name)) { + return Mono.empty(); + } + var listOptions = new ListOptions(); + listOptions.setFieldSelector(FieldSelector.of( + equal("spec.children", name) + )); + return client.listBy(Category.class, listOptions, + PageRequestImpl.of(1, 1, defaultSort()) + ) + .flatMap(result -> Mono.justOrEmpty(ListResult.first(result))); + } + + @Override + public Mono isCategoryHidden(@NonNull String categoryName) { + return client.fetch(Category.class, categoryName) + .expand(category -> getParentByName(category.getMetadata().getName())) + .filter(category -> category.getSpec().isHideFromList()) + .hasElements(); + } + + static Sort defaultSort() { + return Sort.by(Sort.Order.desc("spec.priority"), + Sort.Order.desc("metadata.creationTimestamp"), + Sort.Order.desc("metadata.name")); + } + private boolean isNotIndependent(Category category) { return !category.getSpec().isPreventParentPostCascadeQuery(); } diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/CategoryReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/CategoryReconciler.java index bfd32f007..498757e9e 100644 --- a/application/src/main/java/run/halo/app/core/extension/reconciler/CategoryReconciler.java +++ b/application/src/main/java/run/halo/app/core/extension/reconciler/CategoryReconciler.java @@ -2,17 +2,21 @@ package run.halo.app.core.extension.reconciler; import static run.halo.app.extension.ExtensionUtil.addFinalizers; import static run.halo.app.extension.ExtensionUtil.removeFinalizers; +import static run.halo.app.extension.MetadataUtil.nullSafeAnnotations; import java.util.Map; import java.util.Set; import lombok.AllArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import run.halo.app.content.CategoryService; import run.halo.app.content.permalinks.CategoryPermalinkPolicy; import run.halo.app.core.extension.content.Category; import run.halo.app.core.extension.content.Constant; +import run.halo.app.event.post.CategoryHiddenStateChangeEvent; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ExtensionUtil; -import run.halo.app.extension.MetadataUtil; import run.halo.app.extension.controller.Controller; import run.halo.app.extension.controller.ControllerBuilder; import run.halo.app.extension.controller.Reconciler; @@ -29,6 +33,8 @@ public class CategoryReconciler implements Reconciler { static final String FINALIZER_NAME = "category-protection"; private final ExtensionClient client; private final CategoryPermalinkPolicy categoryPermalinkPolicy; + private final CategoryService categoryService; + private final ApplicationEventPublisher eventPublisher; @Override public Result reconcile(Request request) { @@ -44,12 +50,52 @@ public class CategoryReconciler implements Reconciler { populatePermalinkPattern(category); populatePermalink(category); + checkHideFromListState(category); client.update(category); }); return Result.doNotRetry(); } + /** + * TODO move this logic to before-create/update hook in the future see {@code gh-4343}. + */ + private void checkHideFromListState(Category category) { + final boolean hidden = categoryService.isCategoryHidden(category.getMetadata().getName()) + .blockOptional() + .orElse(false); + category.getSpec().setHideFromList(hidden); + if (isHiddenStateChanged(category)) { + publishHiddenStateChangeEvent(category); + } + var children = category.getSpec().getChildren(); + if (CollectionUtils.isEmpty(children)) { + return; + } + for (String childName : children) { + client.fetch(Category.class, childName) + .ifPresent(child -> { + child.getSpec().setHideFromList(hidden); + if (isHiddenStateChanged(child)) { + publishHiddenStateChangeEvent(child); + } + client.update(child); + }); + } + } + + private void publishHiddenStateChangeEvent(Category category) { + var hidden = category.getSpec().isHideFromList(); + nullSafeAnnotations(category).put(Category.LAST_HIDDEN_STATE_ANNO, String.valueOf(hidden)); + eventPublisher.publishEvent(new CategoryHiddenStateChangeEvent(this, + category.getMetadata().getName(), hidden)); + } + + boolean isHiddenStateChanged(Category category) { + var lastHiddenState = nullSafeAnnotations(category).get(Category.LAST_HIDDEN_STATE_ANNO); + return !String.valueOf(category.getSpec().isHideFromList()).equals(lastHiddenState); + } + @Override public Controller setupWith(ControllerBuilder builder) { return builder @@ -58,7 +104,7 @@ public class CategoryReconciler implements Reconciler { } void populatePermalinkPattern(Category category) { - Map annotations = MetadataUtil.nullSafeAnnotations(category); + Map annotations = nullSafeAnnotations(category); String newPattern = categoryPermalinkPolicy.pattern(); annotations.put(Constant.PERMALINK_PATTERN_ANNO, newPattern); } diff --git a/application/src/main/java/run/halo/app/event/post/CategoryHiddenStateChangeEvent.java b/application/src/main/java/run/halo/app/event/post/CategoryHiddenStateChangeEvent.java new file mode 100644 index 000000000..92a2d23e5 --- /dev/null +++ b/application/src/main/java/run/halo/app/event/post/CategoryHiddenStateChangeEvent.java @@ -0,0 +1,24 @@ +package run.halo.app.event.post; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; +import run.halo.app.core.extension.content.Category; + +/** + * When the category {@link Category.CategorySpec#isHideFromList()} state changes, this event is + * triggered. + * + * @author guqing + * @since 2.17.0 + */ +@Getter +public class CategoryHiddenStateChangeEvent extends ApplicationEvent { + private final String categoryName; + private final boolean hidden; + + public CategoryHiddenStateChangeEvent(Object source, String categoryName, boolean hidden) { + super(source); + this.categoryName = categoryName; + this.hidden = hidden; + } +} diff --git a/application/src/main/java/run/halo/app/infra/SchemeInitializer.java b/application/src/main/java/run/halo/app/infra/SchemeInitializer.java index 3d8089393..072113429 100644 --- a/application/src/main/java/run/halo/app/infra/SchemeInitializer.java +++ b/application/src/main/java/run/halo/app/infra/SchemeInitializer.java @@ -192,6 +192,14 @@ public class SchemeInitializer implements ApplicationListener { + var hidden = post.getStatus().getHideFromList(); + // only index when hidden is true + return (hidden == null || !hidden) ? null : BooleanUtils.TRUE; + })) + ); indexSpecs.add(new IndexSpec() .setName(Post.REQUIRE_SYNC_ON_STARTUP_INDEX_NAME) .setIndexFunc(simpleAttribute(Post.class, post -> { @@ -238,6 +246,19 @@ public class SchemeInitializer implements ApplicationListener defaultIfNull(category.getSpec().getPriority(), 0).toString()))); + indexSpecs.add(new IndexSpec() + .setName("spec.children") + .setIndexFunc(multiValueAttribute(Category.class, category -> { + var children = category.getSpec().getChildren(); + return children == null ? Set.of() : Set.copyOf(children); + })) + ); + indexSpecs.add(new IndexSpec() + .setName("spec.hideFromList") + .setIndexFunc(simpleAttribute(Category.class, + category -> toStringTrueFalse(isTrue(category.getSpec().isHideFromList())) + )) + ); }); schemeManager.register(Tag.class, indexSpecs -> { indexSpecs.add(new IndexSpec() diff --git a/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java b/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java index cbbe0ddc4..af479c116 100644 --- a/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java +++ b/application/src/main/java/run/halo/app/theme/finders/impl/CategoryFinderImpl.java @@ -1,5 +1,7 @@ package run.halo.app.theme.finders.impl; +import static run.halo.app.extension.index.query.QueryFactory.notEqual; + import java.time.Instant; import java.util.Collection; import java.util.Comparator; @@ -8,19 +10,21 @@ import java.util.Map; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Sort; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import run.halo.app.content.CategoryService; import run.halo.app.core.extension.content.Category; import run.halo.app.extension.ListOptions; import run.halo.app.extension.ListResult; import run.halo.app.extension.Metadata; import run.halo.app.extension.PageRequestImpl; import run.halo.app.extension.ReactiveExtensionClient; -import run.halo.app.extension.index.query.QueryFactory; import run.halo.app.extension.router.selector.FieldSelector; import run.halo.app.theme.finders.CategoryFinder; import run.halo.app.theme.finders.Finder; @@ -35,12 +39,10 @@ import run.halo.app.theme.finders.vo.CategoryVo; */ @Slf4j @Finder("categoryFinder") +@RequiredArgsConstructor public class CategoryFinderImpl implements CategoryFinder { private final ReactiveExtensionClient client; - - public CategoryFinderImpl(ReactiveExtensionClient client) { - this.client = client; - } + private final CategoryService categoryService; @Override public Mono getByName(String name) { @@ -65,7 +67,11 @@ public class CategoryFinderImpl implements CategoryFinder { @Override public Mono> list(Integer page, Integer size) { - return client.listBy(Category.class, new ListOptions(), + var listOptions = new ListOptions(); + listOptions.setFieldSelector(FieldSelector.of( + notEqual("spec.hideFromList", BooleanUtils.TRUE) + )); + return client.listBy(Category.class, listOptions, PageRequestImpl.of(pageNullSafe(page), sizeNullSafe(size), defaultSort()) ) .map(list -> { @@ -91,6 +97,7 @@ public class CategoryFinderImpl implements CategoryFinder { @Override public Flux listAll() { return client.listAll(Category.class, new ListOptions(), defaultSort()) + .filter(category -> !category.getSpec().isHideFromList()) .map(CategoryVo::from); } @@ -203,18 +210,8 @@ public class CategoryFinderImpl implements CategoryFinder { @Override public Mono getParentByName(String name) { - if (StringUtils.isBlank(name)) { - return Mono.empty(); - } - var listOptions = new ListOptions(); - listOptions.setFieldSelector(FieldSelector.of( - QueryFactory.equal("spec.children", name) - )); - return client.listBy(Category.class, listOptions, - PageRequestImpl.of(1, 1, defaultSort()) - ) - .map(ListResult::first) - .mapNotNull(item -> item.map(CategoryVo::from).orElse(null)); + return categoryService.getParentByName(name) + .map(CategoryVo::from); } int pageNullSafe(Integer page) { diff --git a/application/src/main/java/run/halo/app/theme/finders/impl/PostFinderImpl.java b/application/src/main/java/run/halo/app/theme/finders/impl/PostFinderImpl.java index 0651c5c6e..bb417d158 100644 --- a/application/src/main/java/run/halo/app/theme/finders/impl/PostFinderImpl.java +++ b/application/src/main/java/run/halo/app/theme/finders/impl/PostFinderImpl.java @@ -3,12 +3,14 @@ package run.halo.app.theme.finders.impl; import static run.halo.app.extension.index.query.QueryFactory.and; import static run.halo.app.extension.index.query.QueryFactory.equal; import static run.halo.app.extension.index.query.QueryFactory.in; +import static run.halo.app.extension.index.query.QueryFactory.notEqual; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import lombok.AllArgsConstructor; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Sort; @@ -136,7 +138,10 @@ public class PostFinderImpl implements PostFinder { @Override public Mono> list(Integer page, Integer size) { - return postPublicQueryService.list(new ListOptions(), getPageRequest(page, size)); + var listOptions = ListOptions.builder() + .fieldQuery(notEqual("status.hideFromList", BooleanUtils.TRUE)) + .build(); + return postPublicQueryService.list(listOptions, getPageRequest(page, size)); } private PageRequestImpl getPageRequest(Integer page, Integer size) { @@ -202,6 +207,9 @@ public class PostFinderImpl implements PostFinder { public Mono> archives(Integer page, Integer size, String year, String month) { var listOptions = new ListOptions(); + listOptions.setFieldSelector(FieldSelector.of( + notEqual("status.hideFromList", BooleanUtils.TRUE)) + ); var labelSelectorBuilder = LabelSelector.builder(); if (StringUtils.isNotBlank(year)) { labelSelectorBuilder.eq(Post.ARCHIVE_YEAR_LABEL, year); diff --git a/application/src/test/java/run/halo/app/theme/finders/impl/CategoryFinderImplTest.java b/application/src/test/java/run/halo/app/theme/finders/impl/CategoryFinderImplTest.java index fee3d0cb1..2156c20df 100644 --- a/application/src/test/java/run/halo/app/theme/finders/impl/CategoryFinderImplTest.java +++ b/application/src/test/java/run/halo/app/theme/finders/impl/CategoryFinderImplTest.java @@ -24,6 +24,7 @@ import org.springframework.data.domain.Sort; import org.springframework.util.ResourceUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import run.halo.app.content.CategoryService; import run.halo.app.core.extension.content.Category; import run.halo.app.extension.ListOptions; import run.halo.app.extension.ListResult; @@ -46,11 +47,14 @@ class CategoryFinderImplTest { @Mock private ReactiveExtensionClient client; + @Mock + private CategoryService categoryService; + private CategoryFinderImpl categoryFinder; @BeforeEach void setUp() { - categoryFinder = new CategoryFinderImpl(client); + categoryFinder = new CategoryFinderImpl(client, categoryService); } @Test @@ -78,7 +82,8 @@ class CategoryFinderImplTest { "C1", "C2" ], - "preventParentPostCascadeQuery": false + "preventParentPostCascadeQuery": false, + "hideFromList": false } } """, diff --git a/ui/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue b/ui/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue index b90c8c448..84394a1fd 100644 --- a/ui/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue +++ b/ui/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue @@ -26,10 +26,12 @@ const props = withDefaults( defineProps<{ category?: Category; parentCategory?: Category; + isChildLevelCategory: boolean; }>(), { category: undefined, parentCategory: undefined, + isChildLevelCategory: false, } ); @@ -266,6 +268,22 @@ const { handleGenerateSlug } = useSlugify( :accepts="['image/*']" validation="length:0,1024" > + (), {}); + const categories = defineModel({ type: Array as PropType, default: [], @@ -95,6 +98,7 @@ const handleDelete = async (category: CategoryTree) => { > { /> + + +