From 9d5529b8270494c2b18a573e3cd31e87031b3309 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Mon, 16 Jan 2023 16:02:13 +0800 Subject: [PATCH] feat: add full-functional post category select component for formkit (#818) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind feature #### What this PR does / why we need it: 添加功能更为全面的文章分类选择器,支持以下特性: 1. 按层级展示分类 2. 支持搜索 3. 选中结果支持显示层级 todo list: - [x] 样式整理 - [x] 支持创建新分类 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2670 Fixes https://github.com/halo-dev/halo/issues/2485 #### Screenshots: image image #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note 重构 Console 端的文章分类选择器。 ``` --- docs/custom-formkit-input/README.md | 2 + src/formkit/formkit.config.ts | 2 +- src/formkit/inputs/category-select.ts | 24 -- .../inputs/category-select/CategorySelect.vue | 315 ++++++++++++++++++ .../components/CategoryListItem.vue | 68 ++++ .../components/CategoryTag.vue | 46 +++ .../components/SearchResultListItem.vue | 67 ++++ src/formkit/inputs/category-select/index.ts | 37 ++ .../inputs/category-select/sections/index.ts | 11 + src/formkit/theme.ts | 11 + .../contents/posts/categories/utils/index.ts | 23 ++ .../posts/components/PostSettingModal.vue | 3 +- 12 files changed, 583 insertions(+), 26 deletions(-) delete mode 100644 src/formkit/inputs/category-select.ts create mode 100644 src/formkit/inputs/category-select/CategorySelect.vue create mode 100644 src/formkit/inputs/category-select/components/CategoryListItem.vue create mode 100644 src/formkit/inputs/category-select/components/CategoryTag.vue create mode 100644 src/formkit/inputs/category-select/components/SearchResultListItem.vue create mode 100644 src/formkit/inputs/category-select/index.ts create mode 100644 src/formkit/inputs/category-select/sections/index.ts diff --git a/docs/custom-formkit-input/README.md b/docs/custom-formkit-input/README.md index caa60e38..39d1084c 100644 --- a/docs/custom-formkit-input/README.md +++ b/docs/custom-formkit-input/README.md @@ -20,6 +20,8 @@ - `postSelect`:选择文章 - `singlePageSelect`:选择自定义页面 - `categorySelect`:选择分类 + - 参数 + 1. multiple: 是否多选,默认为 `false` - `categoryCheckbox`:选择多个分类 - `tagSelect`:选择标签 - 参数 diff --git a/src/formkit/formkit.config.ts b/src/formkit/formkit.config.ts index c5431313..5b302894 100644 --- a/src/formkit/formkit.config.ts +++ b/src/formkit/formkit.config.ts @@ -11,10 +11,10 @@ import { menuRadio } from "./inputs/menu-radio"; import { menuItemSelect } from "./inputs/menu-item-select"; import { postSelect } from "./inputs/post-select"; import { singlePageSelect } from "./inputs/singlePage-select"; +import { tagSelect } from "./inputs/tag-select"; import { categorySelect } from "./inputs/category-select"; import { categoryCheckbox } from "./inputs/category-checkbox"; import { tagCheckbox } from "./inputs/tag-checkbox"; -import { tagSelect } from "./inputs/tag-select/index"; import radioAlt from "./plugins/radio-alt"; import stopImplicitSubmission from "./plugins/stop-implicit-submission"; diff --git a/src/formkit/inputs/category-select.ts b/src/formkit/inputs/category-select.ts deleted file mode 100644 index 35a42869..00000000 --- a/src/formkit/inputs/category-select.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { apiClient } from "@/utils/api-client"; -import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core"; -import { select, selects, defaultIcon } from "@formkit/inputs"; - -function optionsHandler(node: FormKitNode) { - node.on("created", async () => { - const { data } = - await apiClient.extension.category.listcontentHaloRunV1alpha1Category(); - - node.props.options = data.items.map((category) => { - return { - value: category.metadata.name, - label: category.spec.displayName, - }; - }); - }); -} - -export const categorySelect: FormKitTypeDefinition = { - ...select, - props: ["placeholder"], - forceTypeProp: "select", - features: [optionsHandler, selects, defaultIcon("select", "select")], -}; diff --git a/src/formkit/inputs/category-select/CategorySelect.vue b/src/formkit/inputs/category-select/CategorySelect.vue new file mode 100644 index 00000000..cd62d4cb --- /dev/null +++ b/src/formkit/inputs/category-select/CategorySelect.vue @@ -0,0 +1,315 @@ + + + diff --git a/src/formkit/inputs/category-select/components/CategoryListItem.vue b/src/formkit/inputs/category-select/components/CategoryListItem.vue new file mode 100644 index 00000000..eb5f9a40 --- /dev/null +++ b/src/formkit/inputs/category-select/components/CategoryListItem.vue @@ -0,0 +1,68 @@ + + + diff --git a/src/formkit/inputs/category-select/components/CategoryTag.vue b/src/formkit/inputs/category-select/components/CategoryTag.vue new file mode 100644 index 00000000..37a19b40 --- /dev/null +++ b/src/formkit/inputs/category-select/components/CategoryTag.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/formkit/inputs/category-select/components/SearchResultListItem.vue b/src/formkit/inputs/category-select/components/SearchResultListItem.vue new file mode 100644 index 00000000..5e7c80c7 --- /dev/null +++ b/src/formkit/inputs/category-select/components/SearchResultListItem.vue @@ -0,0 +1,67 @@ + + + diff --git a/src/formkit/inputs/category-select/index.ts b/src/formkit/inputs/category-select/index.ts new file mode 100644 index 00000000..6507627f --- /dev/null +++ b/src/formkit/inputs/category-select/index.ts @@ -0,0 +1,37 @@ +import type { FormKitTypeDefinition } from "@formkit/core"; +import { + help, + icon, + inner, + label, + message, + messages, + outer, + prefix, + suffix, + wrapper, +} from "@formkit/inputs"; +import CategorySelect from "./CategorySelect.vue"; +import { CategorySelectSection } from "./sections"; + +export const categorySelect: FormKitTypeDefinition = { + schema: outer( + wrapper( + label("$label"), + inner( + icon("prefix"), + prefix(), + CategorySelectSection(), + suffix(), + icon("suffix") + ) + ), + help("$help"), + messages(message("$message.value")) + ), + type: "input", + props: ["multiple"], + library: { + CategorySelect: CategorySelect, + }, +}; diff --git a/src/formkit/inputs/category-select/sections/index.ts b/src/formkit/inputs/category-select/sections/index.ts new file mode 100644 index 00000000..d0ef0cb5 --- /dev/null +++ b/src/formkit/inputs/category-select/sections/index.ts @@ -0,0 +1,11 @@ +import { createSection } from "@formkit/inputs"; + +export const CategorySelectSection = createSection( + "CategorySelectSection", + () => ({ + $cmp: "CategorySelect", + props: { + context: "$node.context", + }, + }) +); diff --git a/src/formkit/theme.ts b/src/formkit/theme.ts index d95a4a3b..3e7b386a 100644 --- a/src/formkit/theme.ts +++ b/src/formkit/theme.ts @@ -104,6 +104,17 @@ const theme: Record> = { "dropdown-wrapper": "absolute ring-1 ring-white top-full bottom-auto right-0 z-10 mt-1 max-h-96 w-full overflow-auto rounded bg-white shadow-lg", }, + categorySelect: { + ...textClassification, + inner: `${textClassification.inner} !overflow-visible !h-auto min-h-[2.25rem]`, + input: `w-0 flex-grow outline-0 bg-transparent py-1 px-3 block transition-all appearance-none text-sm antialiased`, + "post-categories-wrapper": "flex w-full items-center", + "post-categories": "flex w-full flex-wrap items-center", + "post-categories-button": + "inline-flex h-full cursor-pointer items-center px-1", + "dropdown-wrapper": + "absolute ring-1 ring-white top-full bottom-auto right-0 z-10 mt-1 max-h-96 w-full overflow-auto rounded bg-white shadow-lg", + }, }; export default theme; diff --git a/src/modules/contents/posts/categories/utils/index.ts b/src/modules/contents/posts/categories/utils/index.ts index 0a552d6d..13a9768c 100644 --- a/src/modules/contents/posts/categories/utils/index.ts +++ b/src/modules/contents/posts/categories/utils/index.ts @@ -121,3 +121,26 @@ export function convertCategoryTreeToCategory( }, }; } + +export const getCategoryPath = ( + categories: CategoryTree[], + name: string, + path: CategoryTree[] = [] +) => { + for (const category of categories) { + if (category.metadata && category.metadata.name === name) { + return path.concat([category]); + } + + if (category.spec && category.spec.children) { + const found = getCategoryPath( + category.spec.children, + name, + path.concat([category]) + ); + if (found) { + return found; + } + } + } +}; diff --git a/src/modules/contents/posts/components/PostSettingModal.vue b/src/modules/contents/posts/components/PostSettingModal.vue index 7a4259f0..d7eb79bf 100644 --- a/src/modules/contents/posts/components/PostSettingModal.vue +++ b/src/modules/contents/posts/components/PostSettingModal.vue @@ -270,7 +270,8 @@ const annotationsFormRef = ref>(); v-model="formState.spec.categories" label="分类目录" name="categories" - type="categoryCheckbox" + type="categorySelect" + :multiple="true" />