mirror of https://github.com/halo-dev/halo
feat: add some formkit custom input for the system core extensions (halo-dev/console#643)
#### What type of PR is this? /kind feature /milestone 2.0 #### What this PR does / why we need it: Ref https://github.com/halo-dev/halo/issues/2526#issuecomment-1273094868 FormKit 文档:https://formkit.com/advanced/custom-inputs 通过扩展 FormKit 的自定义 Input,提供系统常用资源的选择组件。 目前提供如下类型: - menuCheckbox - menuRadio - menuItemSelect - postSelect - categorySelect - tagSelect - singlePageSelect - categoryCheckbox - tagCheckbox FormKit 组件的使用方式: ```vue <FormKit placeholder="请选择文章" label="文章" type="postSelect" validation="required" /> ``` FormKit Schema 的使用方式: ```yaml - $formkit: menuRadio name: menus label: 底部菜单组 ``` #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2526 #### Screenshots: <!-- 如果此 PR 有 UI 的改动,最好截图说明这个 PR 的改动。 If there are UI changes to this PR, it is best to take a screenshot to illustrate the changes to this PR. eg. Before:  After:  --> #### Special notes for your reviewer: /cc @halo-dev/sig-halo-console 测试方式: 1. 检查后台文章设置弹框的选择分类和标签功能是否正常。 2. 检查后台添加菜单项的功能是否正常。 3. 使用主题或者插件定义 settings.yaml,使用上述任意 input 类型,检查得到的效果和值是否正常。 #### Does this PR introduce a user-facing change? ```release-note 通过扩展 FormKit 的自定义 Input,提供系统常用资源的选择组件。 ```pull/3445/head
parent
aa2aa26981
commit
a0512e43bc
|
@ -0,0 +1,45 @@
|
||||||
|
# 自定义 FormKit 输入组件
|
||||||
|
|
||||||
|
## 原由
|
||||||
|
|
||||||
|
目前不管是在 Console 中,还是在插件 / 主题设置表单中,都有可能选择系统当中的资源,所以可以通过自定义 FormKit 组件的方式提供常用的选择器。
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
目前已提供以下类型:
|
||||||
|
|
||||||
|
- `menuCheckbox`:选择一组菜单
|
||||||
|
- `menuRadio`:选择一个菜单
|
||||||
|
- `menuItemSelect`:选择菜单项
|
||||||
|
- `postSelect`:选择文章
|
||||||
|
- `singlePageSelect`:选择自定义页面
|
||||||
|
- `categorySelect`:选择分类
|
||||||
|
- `categoryCheckbox`:选择多个分类
|
||||||
|
- `tagSelect`:选择标签
|
||||||
|
- `tagCheckbox`:选择多个标签
|
||||||
|
|
||||||
|
在 Vue 单组件中使用:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const postName = ref("")
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FormKit
|
||||||
|
v-model="postName"
|
||||||
|
placeholder="请选择文章"
|
||||||
|
label="文章"
|
||||||
|
type="postSelect"
|
||||||
|
validation="required"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
在 FormKit Schema 中使用(插件 / 主题设置表单定义):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- $formkit: menuRadio
|
||||||
|
name: menus
|
||||||
|
label: 底部菜单组
|
||||||
|
```
|
|
@ -4,6 +4,15 @@ import { createAutoAnimatePlugin } from "@formkit/addons";
|
||||||
import { zh } from "@formkit/i18n";
|
import { zh } from "@formkit/i18n";
|
||||||
import type { DefaultConfigOptions } from "@formkit/vue";
|
import type { DefaultConfigOptions } from "@formkit/vue";
|
||||||
import { form } from "./inputs/form";
|
import { form } from "./inputs/form";
|
||||||
|
import { menuCheckbox } from "./inputs/menu-checkbox";
|
||||||
|
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 { categorySelect } from "./inputs/category-select";
|
||||||
|
import { tagSelect } from "./inputs/tag-select";
|
||||||
|
import { categoryCheckbox } from "./inputs/category-checkbox";
|
||||||
|
import { tagCheckbox } from "./inputs/tag-checkbox";
|
||||||
|
|
||||||
const config: DefaultConfigOptions = {
|
const config: DefaultConfigOptions = {
|
||||||
config: {
|
config: {
|
||||||
|
@ -12,6 +21,15 @@ const config: DefaultConfigOptions = {
|
||||||
plugins: [createAutoAnimatePlugin()],
|
plugins: [createAutoAnimatePlugin()],
|
||||||
inputs: {
|
inputs: {
|
||||||
form,
|
form,
|
||||||
|
menuCheckbox,
|
||||||
|
menuRadio,
|
||||||
|
menuItemSelect,
|
||||||
|
postSelect,
|
||||||
|
categorySelect,
|
||||||
|
tagSelect,
|
||||||
|
singlePageSelect,
|
||||||
|
categoryCheckbox,
|
||||||
|
tagCheckbox,
|
||||||
},
|
},
|
||||||
locales: { zh },
|
locales: { zh },
|
||||||
locale: "zh",
|
locale: "zh",
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
|
import { checkbox, checkboxes, 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 categoryCheckbox: FormKitTypeDefinition = {
|
||||||
|
...checkbox,
|
||||||
|
props: ["onValue", "offValue"],
|
||||||
|
forceTypeProp: "checkbox",
|
||||||
|
features: [
|
||||||
|
optionsHandler,
|
||||||
|
checkboxes,
|
||||||
|
defaultIcon("decorator", "checkboxDecorator"),
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
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")],
|
||||||
|
};
|
|
@ -31,6 +31,7 @@ export const form: FormKitTypeDefinition = {
|
||||||
"submitBehavior",
|
"submitBehavior",
|
||||||
"incompleteMessage",
|
"incompleteMessage",
|
||||||
],
|
],
|
||||||
|
forceTypeProp: "form",
|
||||||
/**
|
/**
|
||||||
* Additional features that should be added to your input
|
* Additional features that should be added to your input
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
|
import { checkbox, checkboxes, defaultIcon } from "@formkit/inputs";
|
||||||
|
|
||||||
|
function optionsHandler(node: FormKitNode) {
|
||||||
|
node.on("created", async () => {
|
||||||
|
const { data } = await apiClient.extension.menu.listv1alpha1Menu();
|
||||||
|
|
||||||
|
node.props.options = data.items.map((menu) => {
|
||||||
|
return {
|
||||||
|
value: menu.metadata.name,
|
||||||
|
label: menu.spec.displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const menuCheckbox: FormKitTypeDefinition = {
|
||||||
|
...checkbox,
|
||||||
|
props: ["onValue", "offValue"],
|
||||||
|
forceTypeProp: "checkbox",
|
||||||
|
features: [
|
||||||
|
optionsHandler,
|
||||||
|
checkboxes,
|
||||||
|
defaultIcon("decorator", "checkboxDecorator"),
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
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.menuItem.listv1alpha1MenuItem({
|
||||||
|
fieldSelector: [`name=(${node.props.menuItems.join(",")})`],
|
||||||
|
});
|
||||||
|
|
||||||
|
node.props.options = data.items.map((menuItem) => {
|
||||||
|
return {
|
||||||
|
value: menuItem.metadata.name,
|
||||||
|
label: menuItem.status?.displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const menuItemSelect: FormKitTypeDefinition = {
|
||||||
|
...select,
|
||||||
|
props: ["placeholder", "menuItems"],
|
||||||
|
forceTypeProp: "select",
|
||||||
|
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
|
import { radio, radios, defaultIcon } from "@formkit/inputs";
|
||||||
|
|
||||||
|
function optionsHandler(node: FormKitNode) {
|
||||||
|
node.on("created", async () => {
|
||||||
|
const { data } = await apiClient.extension.menu.listv1alpha1Menu();
|
||||||
|
|
||||||
|
node.props.options = data.items.map((menu) => {
|
||||||
|
return {
|
||||||
|
value: menu.metadata.name,
|
||||||
|
label: menu.spec.displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const menuRadio: FormKitTypeDefinition = {
|
||||||
|
...radio,
|
||||||
|
props: ["onValue", "offValue"],
|
||||||
|
forceTypeProp: "radio",
|
||||||
|
features: [
|
||||||
|
optionsHandler,
|
||||||
|
radios,
|
||||||
|
defaultIcon("decorator", "radioDecorator"),
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
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.post.listcontentHaloRunV1alpha1Post();
|
||||||
|
|
||||||
|
node.props.options = data.items.map((post) => {
|
||||||
|
return {
|
||||||
|
value: post.metadata.name,
|
||||||
|
label: post.spec.title,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const postSelect: FormKitTypeDefinition = {
|
||||||
|
...select,
|
||||||
|
props: ["placeholder"],
|
||||||
|
forceTypeProp: "select",
|
||||||
|
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
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.singlePage.listcontentHaloRunV1alpha1SinglePage();
|
||||||
|
|
||||||
|
node.props.options = data.items.map((singlePage) => {
|
||||||
|
return {
|
||||||
|
value: singlePage.metadata.name,
|
||||||
|
label: singlePage.spec.title,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const singlePageSelect: FormKitTypeDefinition = {
|
||||||
|
...select,
|
||||||
|
props: ["placeholder"],
|
||||||
|
forceTypeProp: "select",
|
||||||
|
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { FormKitNode, FormKitTypeDefinition } from "@formkit/core";
|
||||||
|
import { checkbox, checkboxes, defaultIcon } from "@formkit/inputs";
|
||||||
|
|
||||||
|
function optionsHandler(node: FormKitNode) {
|
||||||
|
node.on("created", async () => {
|
||||||
|
const { data } =
|
||||||
|
await apiClient.extension.tag.listcontentHaloRunV1alpha1Tag();
|
||||||
|
|
||||||
|
node.props.options = data.items.map((tag) => {
|
||||||
|
return {
|
||||||
|
value: tag.metadata.name,
|
||||||
|
label: tag.spec.displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tagCheckbox: FormKitTypeDefinition = {
|
||||||
|
...checkbox,
|
||||||
|
props: ["onValue", "offValue"],
|
||||||
|
forceTypeProp: "checkbox",
|
||||||
|
features: [
|
||||||
|
optionsHandler,
|
||||||
|
checkboxes,
|
||||||
|
defaultIcon("decorator", "checkboxDecorator"),
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
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.tag.listcontentHaloRunV1alpha1Tag();
|
||||||
|
|
||||||
|
node.props.options = data.items.map((tag) => {
|
||||||
|
return {
|
||||||
|
value: tag.metadata.name,
|
||||||
|
label: tag.spec.displayName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tagSelect: FormKitTypeDefinition = {
|
||||||
|
...select,
|
||||||
|
props: ["placeholder"],
|
||||||
|
forceTypeProp: "select",
|
||||||
|
features: [optionsHandler, selects, defaultIcon("select", "select")],
|
||||||
|
};
|
|
@ -1,10 +1,8 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { VButton, VModal, VSpace, VTabItem, VTabs } from "@halo-dev/components";
|
import { VButton, VModal, VSpace, VTabItem, VTabs } from "@halo-dev/components";
|
||||||
import { computed, ref, watch, watchEffect } from "vue";
|
import { computed, ref, watchEffect } from "vue";
|
||||||
import type { PostRequest } from "@halo-dev/api-client";
|
import type { PostRequest } from "@halo-dev/api-client";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
|
|
||||||
import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
|
@ -67,26 +65,6 @@ const saving = ref(false);
|
||||||
const publishing = ref(false);
|
const publishing = ref(false);
|
||||||
const publishCanceling = ref(false);
|
const publishCanceling = ref(false);
|
||||||
|
|
||||||
const { categories, handleFetchCategories } = usePostCategory();
|
|
||||||
const categoriesMap = computed(() => {
|
|
||||||
return categories.value.map((category) => {
|
|
||||||
return {
|
|
||||||
value: category.metadata.name,
|
|
||||||
label: category.spec.displayName,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const { tags, handleFetchTags } = usePostTag();
|
|
||||||
const tagsMap = computed(() => {
|
|
||||||
return tags.value.map((tag) => {
|
|
||||||
return {
|
|
||||||
value: tag.metadata.name,
|
|
||||||
label: tag.spec.displayName,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const isUpdateMode = computed(() => {
|
const isUpdateMode = computed(() => {
|
||||||
return !!formState.value.post.metadata.creationTimestamp;
|
return !!formState.value.post.metadata.creationTimestamp;
|
||||||
});
|
});
|
||||||
|
@ -180,16 +158,6 @@ const handlePublishCanceling = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.visible,
|
|
||||||
(visible) => {
|
|
||||||
if (visible) {
|
|
||||||
handleFetchCategories();
|
|
||||||
handleFetchTags();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (props.post) {
|
if (props.post) {
|
||||||
formState.value = cloneDeep(props.post);
|
formState.value = cloneDeep(props.post);
|
||||||
|
@ -233,17 +201,15 @@ watchEffect(() => {
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.post.spec.categories"
|
v-model="formState.post.spec.categories"
|
||||||
:options="categoriesMap"
|
|
||||||
label="分类目录"
|
label="分类目录"
|
||||||
name="categories"
|
name="categories"
|
||||||
type="checkbox"
|
type="categoryCheckbox"
|
||||||
/>
|
/>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.post.spec.tags"
|
v-model="formState.post.spec.tags"
|
||||||
:options="tagsMap"
|
|
||||||
label="标签"
|
label="标签"
|
||||||
name="tags"
|
name="tags"
|
||||||
type="checkbox"
|
type="tagCheckbox"
|
||||||
/>
|
/>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.post.spec.excerpt.autoGenerate"
|
v-model="formState.post.spec.excerpt.autoGenerate"
|
||||||
|
|
|
@ -2,15 +2,12 @@
|
||||||
import { VButton, VModal, VSpace } from "@halo-dev/components";
|
import { VButton, VModal, VSpace } from "@halo-dev/components";
|
||||||
import SubmitButton from "@/components/button/SubmitButton.vue";
|
import SubmitButton from "@/components/button/SubmitButton.vue";
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import type { Menu, MenuItem, Post, SinglePage } from "@halo-dev/api-client";
|
import type { Menu, MenuItem } from "@halo-dev/api-client";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
import { reset } from "@formkit/core";
|
import { reset } from "@formkit/core";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
|
|
||||||
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
|
|
||||||
import { setFocus } from "@/formkit/utils/focus";
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
import type { FormKitOptionsProp } from "@formkit/inputs";
|
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -47,7 +44,6 @@ const initialFormState: MenuItem = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuItemMap = ref<FormKitOptionsProp>();
|
|
||||||
const selectedParentMenuItem = ref<string>("");
|
const selectedParentMenuItem = ref<string>("");
|
||||||
const formState = ref<MenuItem>(cloneDeep(initialFormState));
|
const formState = ref<MenuItem>(cloneDeep(initialFormState));
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
|
@ -56,25 +52,6 @@ const isUpdateMode = computed(() => {
|
||||||
return !!formState.value.metadata.creationTimestamp;
|
return !!formState.value.metadata.creationTimestamp;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleFetchMenuItems = async () => {
|
|
||||||
try {
|
|
||||||
const { data } = await apiClient.extension.menuItem.listv1alpha1MenuItem({
|
|
||||||
fieldSelector: [`name=(${props.menu?.spec.menuItems?.join(",")})`],
|
|
||||||
});
|
|
||||||
menuItemMap.value = [
|
|
||||||
{ label: "无", value: undefined },
|
|
||||||
...data.items.map((menuItem) => {
|
|
||||||
return {
|
|
||||||
label: menuItem.status?.displayName as string,
|
|
||||||
value: menuItem.metadata.name,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Failed to fetch menu items", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveMenuItem = async () => {
|
const handleSaveMenuItem = async () => {
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
|
@ -244,104 +221,11 @@ const menuItemSources: MenuItemSource[] = [
|
||||||
|
|
||||||
const selectedMenuItemSource = ref<string>(menuItemSources[0].value);
|
const selectedMenuItemSource = ref<string>(menuItemSources[0].value);
|
||||||
|
|
||||||
const { categories, handleFetchCategories } = usePostCategory();
|
|
||||||
const { tags, handleFetchTags } = usePostTag();
|
|
||||||
const posts = ref<Post[]>([] as Post[]);
|
|
||||||
const singlePages = ref<SinglePage[]>([] as SinglePage[]);
|
|
||||||
|
|
||||||
const postMap = computed(() => {
|
|
||||||
return [
|
|
||||||
{ label: "请选择文章", value: undefined },
|
|
||||||
...posts.value.map((post) => {
|
|
||||||
return {
|
|
||||||
label: post.spec.title,
|
|
||||||
value: post.metadata.name,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const singlePageMap = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: "请选择自定义页面",
|
|
||||||
value: undefined,
|
|
||||||
},
|
|
||||||
...singlePages.value.map((singlePage) => {
|
|
||||||
return {
|
|
||||||
label: singlePage.spec.title,
|
|
||||||
value: singlePage.metadata.name,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const categoryMap = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: "请选择分类",
|
|
||||||
value: undefined,
|
|
||||||
},
|
|
||||||
...categories.value.map((category) => {
|
|
||||||
return {
|
|
||||||
label: category.spec.displayName,
|
|
||||||
value: category.metadata.name,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const tagMap = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: "请选择标签",
|
|
||||||
value: undefined,
|
|
||||||
},
|
|
||||||
...tags.value.map((tag) => {
|
|
||||||
return {
|
|
||||||
label: tag.spec.displayName,
|
|
||||||
value: tag.metadata.name,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectedRef = ref<string>("");
|
const selectedRef = ref<string>("");
|
||||||
|
|
||||||
const handleFetchPosts = async () => {
|
|
||||||
const { data } =
|
|
||||||
await apiClient.extension.post.listcontentHaloRunV1alpha1Post({
|
|
||||||
page: 0,
|
|
||||||
size: 0,
|
|
||||||
});
|
|
||||||
posts.value = data.items;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFetchSinglePages = async () => {
|
|
||||||
const { data } =
|
|
||||||
await apiClient.extension.singlePage.listcontentHaloRunV1alpha1SinglePage({
|
|
||||||
page: 0,
|
|
||||||
size: 0,
|
|
||||||
});
|
|
||||||
singlePages.value = data.items;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMenuItemSourceChange = () => {
|
const onMenuItemSourceChange = () => {
|
||||||
selectedRef.value = "";
|
selectedRef.value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.visible,
|
|
||||||
(newValue) => {
|
|
||||||
if (newValue) {
|
|
||||||
handleFetchMenuItems();
|
|
||||||
handleFetchCategories();
|
|
||||||
handleFetchTags();
|
|
||||||
handleFetchPosts();
|
|
||||||
handleFetchSinglePages();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VModal
|
<VModal
|
||||||
|
@ -359,11 +243,12 @@ watch(
|
||||||
@submit="handleSaveMenuItem"
|
@submit="handleSaveMenuItem"
|
||||||
>
|
>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-if="!isUpdateMode && menuItemMap"
|
v-if="!isUpdateMode && menu"
|
||||||
v-model="selectedParentMenuItem"
|
v-model="selectedParentMenuItem"
|
||||||
label="上级菜单项"
|
label="上级菜单项"
|
||||||
type="select"
|
placeholder="选择上级菜单项"
|
||||||
:options="menuItemMap"
|
type="menuItemSelect"
|
||||||
|
:menu-items="menu?.spec.menuItems || []"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
|
@ -397,9 +282,9 @@ watch(
|
||||||
<FormKit
|
<FormKit
|
||||||
v-if="selectedMenuItemSource === 'post'"
|
v-if="selectedMenuItemSource === 'post'"
|
||||||
v-model="selectedRef"
|
v-model="selectedRef"
|
||||||
|
placeholder="请选择文章"
|
||||||
label="文章"
|
label="文章"
|
||||||
type="select"
|
type="postSelect"
|
||||||
:options="postMap"
|
|
||||||
validation="required"
|
validation="required"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -407,26 +292,25 @@ watch(
|
||||||
v-if="selectedMenuItemSource === 'singlePage'"
|
v-if="selectedMenuItemSource === 'singlePage'"
|
||||||
v-model="selectedRef"
|
v-model="selectedRef"
|
||||||
label="自定义页面"
|
label="自定义页面"
|
||||||
type="select"
|
type="singlePageSelect"
|
||||||
:options="singlePageMap"
|
|
||||||
validation="required"
|
validation="required"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
v-if="selectedMenuItemSource === 'tag'"
|
v-if="selectedMenuItemSource === 'tag'"
|
||||||
v-model="selectedRef"
|
v-model="selectedRef"
|
||||||
|
placeholder="请选择标签"
|
||||||
label="标签"
|
label="标签"
|
||||||
type="select"
|
type="tagSelect"
|
||||||
:options="tagMap"
|
|
||||||
validation="required"
|
validation="required"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
v-if="selectedMenuItemSource === 'category'"
|
v-if="selectedMenuItemSource === 'category'"
|
||||||
v-model="selectedRef"
|
v-model="selectedRef"
|
||||||
|
placeholder="请选择分类"
|
||||||
label="分类"
|
label="分类"
|
||||||
type="select"
|
type="categorySelect"
|
||||||
:options="categoryMap"
|
|
||||||
validation="required"
|
validation="required"
|
||||||
/>
|
/>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
Loading…
Reference in New Issue