mirror of https://github.com/halo-dev/halo-admin
feat: menu items support setting ref relations (#604)
<!-- Thanks for sending a pull request! Here are some tips for you: 1. 如果这是你的第一次,请阅读我们的贡献指南:<https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>。 1. If this is your first time, please read our contributor guidelines: <https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>. 2. 请根据你解决问题的类型为 Pull Request 添加合适的标签。 2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. 3. 请确保你已经添加并运行了适当的测试。 3. Ensure you have added or ran the appropriate tests for your PR. --> #### What type of PR is this? /kind feature /milestone 2.0 <!-- 添加其中一个类别: Add one of the following kinds: /kind bug /kind cleanup /kind documentation /kind feature /kind optimization 适当添加其中一个或多个类别(可选): Optionally add one or more of the following kinds if applicable: /kind api-change /kind deprecation /kind failing-test /kind flake /kind regression --> #### What this PR does / why we need it: 菜单项支持设置与文章、分类、标签的关联关系。关联之后,当被关联对象有所改变时,同步菜单项更新。适配:https://github.com/halo-dev/halo/pull/2380 > 自定义页面(pageRef)会等到 https://github.com/halo-dev/halo/pull/2381 合并之后再做适配。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2295 <!-- PR 合并时自动关闭 issue。 Automatically closes linked issue when PR is merged. 用法:`Fixes #<issue 号>`,或者 `Fixes (粘贴 issue 完整链接)` Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`. --> #### Screenshots: <img width="1389" alt="image" src="https://user-images.githubusercontent.com/21301288/188453129-26711a32-707f-4c45-a137-fa386beff6a3.png"> <img width="1389" alt="image" src="https://user-images.githubusercontent.com/21301288/188453143-4c32ae32-3910-49a1-9a1f-1ae51f596c99.png"> <!-- 如果此 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: /hold until https://github.com/halo-dev/halo/pull/2380 merge #### Does this PR introduce a user-facing change? <!-- 如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。 否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change), Release Note 需要以 `action required` 开头。 If no, just write "NONE" in the release-note block below. If yes, a release note is required: Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required". --> ```release-note None ```pull/607/head
parent
f2a1f3a303
commit
a57e832816
|
@ -33,7 +33,7 @@
|
||||||
"@formkit/themes": "1.0.0-beta.10",
|
"@formkit/themes": "1.0.0-beta.10",
|
||||||
"@formkit/vue": "1.0.0-beta.10",
|
"@formkit/vue": "1.0.0-beta.10",
|
||||||
"@halo-dev/admin-shared": "workspace:*",
|
"@halo-dev/admin-shared": "workspace:*",
|
||||||
"@halo-dev/api-client": "^0.0.14",
|
"@halo-dev/api-client": "^0.0.15",
|
||||||
"@halo-dev/components": "workspace:*",
|
"@halo-dev/components": "workspace:*",
|
||||||
"@halo-dev/richtext-editor": "^0.0.0-alpha.5",
|
"@halo-dev/richtext-editor": "^0.0.0-alpha.5",
|
||||||
"@tiptap/extension-character-count": "2.0.0-beta.31",
|
"@tiptap/extension-character-count": "2.0.0-beta.31",
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
"homepage": "https://github.com/halo-dev/halo-admin/tree/next/shared/components#readme",
|
"homepage": "https://github.com/halo-dev/halo-admin/tree/next/shared/components#readme",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@halo-dev/api-client": "^0.0.14",
|
"@halo-dev/api-client": "^0.0.15",
|
||||||
"@halo-dev/components": "workspace:*",
|
"@halo-dev/components": "workspace:*",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"lodash.merge": "^4.6.2"
|
"lodash.merge": "^4.6.2"
|
||||||
|
|
|
@ -13,7 +13,7 @@ importers:
|
||||||
'@formkit/themes': 1.0.0-beta.10
|
'@formkit/themes': 1.0.0-beta.10
|
||||||
'@formkit/vue': 1.0.0-beta.10
|
'@formkit/vue': 1.0.0-beta.10
|
||||||
'@halo-dev/admin-shared': workspace:*
|
'@halo-dev/admin-shared': workspace:*
|
||||||
'@halo-dev/api-client': ^0.0.14
|
'@halo-dev/api-client': ^0.0.15
|
||||||
'@halo-dev/components': workspace:*
|
'@halo-dev/components': workspace:*
|
||||||
'@halo-dev/richtext-editor': ^0.0.0-alpha.5
|
'@halo-dev/richtext-editor': ^0.0.0-alpha.5
|
||||||
'@iconify-json/vscode-icons': ^1.1.11
|
'@iconify-json/vscode-icons': ^1.1.11
|
||||||
|
@ -92,7 +92,7 @@ importers:
|
||||||
'@formkit/themes': 1.0.0-beta.10_tailwindcss@3.1.8
|
'@formkit/themes': 1.0.0-beta.10_tailwindcss@3.1.8
|
||||||
'@formkit/vue': 1.0.0-beta.10_wwmyxdjqen5bmh3tr2meig5lki
|
'@formkit/vue': 1.0.0-beta.10_wwmyxdjqen5bmh3tr2meig5lki
|
||||||
'@halo-dev/admin-shared': link:packages/shared
|
'@halo-dev/admin-shared': link:packages/shared
|
||||||
'@halo-dev/api-client': 0.0.14
|
'@halo-dev/api-client': 0.0.15
|
||||||
'@halo-dev/components': link:packages/components
|
'@halo-dev/components': link:packages/components
|
||||||
'@halo-dev/richtext-editor': 0.0.0-alpha.5_vue@3.2.37
|
'@halo-dev/richtext-editor': 0.0.0-alpha.5_vue@3.2.37
|
||||||
'@tiptap/extension-character-count': 2.0.0-beta.31
|
'@tiptap/extension-character-count': 2.0.0-beta.31
|
||||||
|
@ -194,14 +194,14 @@ importers:
|
||||||
|
|
||||||
packages/shared:
|
packages/shared:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@halo-dev/api-client': ^0.0.14
|
'@halo-dev/api-client': ^0.0.15
|
||||||
'@halo-dev/components': workspace:*
|
'@halo-dev/components': workspace:*
|
||||||
'@types/lodash.merge': ^4.6.7
|
'@types/lodash.merge': ^4.6.7
|
||||||
axios: ^0.27.2
|
axios: ^0.27.2
|
||||||
lodash.merge: ^4.6.2
|
lodash.merge: ^4.6.2
|
||||||
vite-plugin-dts: ^1.4.1
|
vite-plugin-dts: ^1.4.1
|
||||||
dependencies:
|
dependencies:
|
||||||
'@halo-dev/api-client': 0.0.14
|
'@halo-dev/api-client': 0.0.15
|
||||||
'@halo-dev/components': link:../components
|
'@halo-dev/components': link:../components
|
||||||
axios: 0.27.2
|
axios: 0.27.2
|
||||||
lodash.merge: 4.6.2
|
lodash.merge: 4.6.2
|
||||||
|
@ -2129,8 +2129,8 @@ packages:
|
||||||
- windicss
|
- windicss
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@halo-dev/api-client/0.0.14:
|
/@halo-dev/api-client/0.0.15:
|
||||||
resolution: {integrity: sha512-Qh0/l2f5e8lxBgAU2brN28F3CzZTlxGUGY0puUGbuDRYqEENbQ5pGTHG3CzuiG4it4Pn16xSTCXICO6M9m9X4A==}
|
resolution: {integrity: sha512-RCQXU2s5IJJ3pUORg0n2b/CYbDox/Jm4aXB2J9ZbKod3vdS3HM6X33iVXc/pyuxRjucijly3WyLkiTZ0bkwLbg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@halo-dev/richtext-editor/0.0.0-alpha.5_vue@3.2.37:
|
/@halo-dev/richtext-editor/0.0.0-alpha.5_vue@3.2.37:
|
||||||
|
|
|
@ -54,8 +54,8 @@ const checkedAll = ref(false);
|
||||||
const selectedPostNames = ref<string[]>([]);
|
const selectedPostNames = ref<string[]>([]);
|
||||||
|
|
||||||
const { users } = useUserFetch();
|
const { users } = useUserFetch();
|
||||||
const { categories } = usePostCategory();
|
const { categories } = usePostCategory({ fetchOnMounted: true });
|
||||||
const { tags } = usePostTag();
|
const { tags } = usePostTag({ fetchOnMounted: true });
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
|
|
||||||
const handleFetchPosts = async () => {
|
const handleFetchPosts = async () => {
|
||||||
|
|
|
@ -40,7 +40,7 @@ const {
|
||||||
loading,
|
loading,
|
||||||
handleFetchCategories,
|
handleFetchCategories,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
} = usePostCategory();
|
} = usePostCategory({ fetchOnMounted: true });
|
||||||
|
|
||||||
const handleUpdateInBatch = useDebounceFn(async () => {
|
const handleUpdateInBatch = useDebounceFn(async () => {
|
||||||
const categoriesTreeToUpdate = resetCategoriesTreePriority(
|
const categoriesTreeToUpdate = resetCategoriesTreePriority(
|
||||||
|
|
|
@ -14,7 +14,11 @@ interface usePostCategoryReturn {
|
||||||
handleDelete: (category: CategoryTree) => void;
|
handleDelete: (category: CategoryTree) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePostCategory(): usePostCategoryReturn {
|
export function usePostCategory(options?: {
|
||||||
|
fetchOnMounted: boolean;
|
||||||
|
}): usePostCategoryReturn {
|
||||||
|
const { fetchOnMounted } = options || {};
|
||||||
|
|
||||||
const categories = ref<Category[]>([] as Category[]);
|
const categories = ref<Category[]>([] as Category[]);
|
||||||
const categoriesTree = ref<CategoryTree[]>([] as CategoryTree[]);
|
const categoriesTree = ref<CategoryTree[]>([] as CategoryTree[]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
@ -59,7 +63,9 @@ export function usePostCategory(): usePostCategoryReturn {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(handleFetchCategories);
|
onMounted(() => {
|
||||||
|
fetchOnMounted && handleFetchCategories();
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
categories,
|
categories,
|
||||||
|
|
|
@ -67,7 +67,7 @@ const saving = ref(false);
|
||||||
const publishing = ref(false);
|
const publishing = ref(false);
|
||||||
const publishCanceling = ref(false);
|
const publishCanceling = ref(false);
|
||||||
|
|
||||||
const { categories } = usePostCategory();
|
const { categories } = usePostCategory({ fetchOnMounted: true });
|
||||||
const categoriesMap = computed(() => {
|
const categoriesMap = computed(() => {
|
||||||
return categories.value.map((category) => {
|
return categories.value.map((category) => {
|
||||||
return {
|
return {
|
||||||
|
@ -77,7 +77,7 @@ const categoriesMap = computed(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const { tags } = usePostTag();
|
const { tags } = usePostTag({ fetchOnMounted: true });
|
||||||
const tagsMap = computed(() => {
|
const tagsMap = computed(() => {
|
||||||
return tags.value.map((tag) => {
|
return tags.value.map((tag) => {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -40,7 +40,9 @@ const viewTypes = [
|
||||||
|
|
||||||
const viewType = ref("list");
|
const viewType = ref("list");
|
||||||
|
|
||||||
const { tags, loading, handleFetchTags, handleDelete } = usePostTag();
|
const { tags, loading, handleFetchTags, handleDelete } = usePostTag({
|
||||||
|
fetchOnMounted: true,
|
||||||
|
});
|
||||||
|
|
||||||
const editingModal = ref(false);
|
const editingModal = ref(false);
|
||||||
const selectedTag = ref<Tag | null>(null);
|
const selectedTag = ref<Tag | null>(null);
|
||||||
|
|
|
@ -11,7 +11,11 @@ interface usePostTagReturn {
|
||||||
handleDelete: (tag: Tag) => void;
|
handleDelete: (tag: Tag) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePostTag(): usePostTagReturn {
|
export function usePostTag(options?: {
|
||||||
|
fetchOnMounted: boolean;
|
||||||
|
}): usePostTagReturn {
|
||||||
|
const { fetchOnMounted } = options || {};
|
||||||
|
|
||||||
const tags = ref<Tag[]>([] as Tag[]);
|
const tags = ref<Tag[]>([] as Tag[]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
|
@ -53,7 +57,9 @@ export function usePostTag(): usePostTagReturn {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(handleFetchTags);
|
onMounted(() => {
|
||||||
|
fetchOnMounted && handleFetchTags();
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tags,
|
tags,
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { VButton, VModal, VSpace } from "@halo-dev/components";
|
import { VButton, VModal, VSpace } from "@halo-dev/components";
|
||||||
import { computed, ref, watch, watchEffect } from "vue";
|
import { computed, ref, watch, watchEffect } from "vue";
|
||||||
import type { MenuItem } from "@halo-dev/api-client";
|
import type { MenuItem, Post } from "@halo-dev/api-client";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import { apiClient } from "@halo-dev/admin-shared";
|
import { apiClient } from "@halo-dev/admin-shared";
|
||||||
import { reset, submitForm } from "@formkit/core";
|
import { reset, submitForm } from "@formkit/core";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import { useMagicKeys } from "@vueuse/core";
|
import { useMagicKeys } from "@vueuse/core";
|
||||||
|
import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
|
||||||
|
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -50,6 +52,21 @@ const handleSaveMenuItem = async () => {
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
|
|
||||||
|
const menuItemSource = menuItemSources.find(
|
||||||
|
(source) => source.value === selectedMenuItemSource.value
|
||||||
|
);
|
||||||
|
|
||||||
|
if (menuItemSource) {
|
||||||
|
const { ref } = menuItemSource;
|
||||||
|
if (ref) {
|
||||||
|
formState.value.spec[ref] = {
|
||||||
|
version: "content.halo.run/v1alpha1",
|
||||||
|
kind: menuItemSource.kind,
|
||||||
|
name: selectedRef.value as string,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isUpdateMode.value) {
|
if (isUpdateMode.value) {
|
||||||
const { data } =
|
const { data } =
|
||||||
await apiClient.extension.menuItem.updatev1alpha1MenuItem({
|
await apiClient.extension.menuItem.updatev1alpha1MenuItem({
|
||||||
|
@ -80,29 +97,164 @@ const onVisibleChange = (visible: boolean) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(props, (newVal) => {
|
const handleResetForm = () => {
|
||||||
if (newVal.visible && props.menuItem) {
|
|
||||||
formState.value = cloneDeep(props.menuItem);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
formState.value = cloneDeep(initialFormState);
|
formState.value = cloneDeep(initialFormState);
|
||||||
formState.value.metadata.name = uuid();
|
formState.value.metadata.name = uuid();
|
||||||
reset("menuitem-form");
|
reset("menuitem-form");
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const { Command_Enter } = useMagicKeys();
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
let keyboardWatcher;
|
if (Command_Enter.value && props.visible) {
|
||||||
const { Command_Enter } = useMagicKeys();
|
submitForm("menuitem-form");
|
||||||
if (props.visible) {
|
|
||||||
keyboardWatcher = watch(Command_Enter, (v) => {
|
|
||||||
if (v) {
|
|
||||||
submitForm("menuitem-form");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
keyboardWatcher?.unwatch();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(visible) => {
|
||||||
|
if (!visible) {
|
||||||
|
handleResetForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.menuItem,
|
||||||
|
(menuItem) => {
|
||||||
|
if (menuItem) {
|
||||||
|
formState.value = cloneDeep(menuItem);
|
||||||
|
|
||||||
|
// Set Ref related
|
||||||
|
const { postRef, categoryRef, tagRef } = formState.value.spec;
|
||||||
|
|
||||||
|
if (postRef) {
|
||||||
|
selectedMenuItemSource.value = "post";
|
||||||
|
selectedRef.value = postRef.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (categoryRef) {
|
||||||
|
selectedMenuItemSource.value = "category";
|
||||||
|
selectedRef.value = categoryRef.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagRef) {
|
||||||
|
selectedMenuItemSource.value = "tag";
|
||||||
|
selectedRef.value = tagRef.name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleResetForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// MenuItem Ref
|
||||||
|
interface MenuItemSource {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
ref?: "postRef" | "categoryRef" | "tagRef";
|
||||||
|
kind?: "Post" | "Category" | "Tag";
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuItemSources: MenuItemSource[] = [
|
||||||
|
{
|
||||||
|
label: "自定义链接",
|
||||||
|
value: "custom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "文章",
|
||||||
|
value: "post",
|
||||||
|
ref: "postRef",
|
||||||
|
kind: "Post",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "分类",
|
||||||
|
value: "category",
|
||||||
|
ref: "categoryRef",
|
||||||
|
kind: "Post",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "标签",
|
||||||
|
value: "tag",
|
||||||
|
ref: "tagRef",
|
||||||
|
kind: "Tag",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedMenuItemSource = ref<string>(menuItemSources[0].value);
|
||||||
|
|
||||||
|
const { categories, handleFetchCategories } = usePostCategory();
|
||||||
|
const { tags, handleFetchTags } = usePostTag();
|
||||||
|
const posts = ref<Post[]>([] as Post[]);
|
||||||
|
|
||||||
|
const postMap = computed(() => {
|
||||||
|
return [
|
||||||
|
{ label: "请选择文章", value: undefined },
|
||||||
|
...posts.value.map((post) => {
|
||||||
|
return {
|
||||||
|
label: post.spec.title,
|
||||||
|
value: post.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 handleFetchPosts = async () => {
|
||||||
|
const { data } =
|
||||||
|
await apiClient.extension.post.listcontentHaloRunV1alpha1Post({
|
||||||
|
page: 0,
|
||||||
|
size: 0,
|
||||||
|
});
|
||||||
|
posts.value = data.items;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMenuItemSourceChange = () => {
|
||||||
|
selectedRef.value = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
handleFetchCategories();
|
||||||
|
handleFetchTags();
|
||||||
|
handleFetchPosts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VModal
|
<VModal
|
||||||
|
@ -113,17 +265,56 @@ watchEffect(() => {
|
||||||
>
|
>
|
||||||
<FormKit id="menuitem-form" type="form" @submit="handleSaveMenuItem">
|
<FormKit id="menuitem-form" type="form" @submit="handleSaveMenuItem">
|
||||||
<FormKit
|
<FormKit
|
||||||
|
v-model="selectedMenuItemSource"
|
||||||
|
:options="menuItemSources"
|
||||||
|
:disabled="isUpdateMode"
|
||||||
|
label="类型"
|
||||||
|
type="select"
|
||||||
|
@change="onMenuItemSourceChange"
|
||||||
|
>
|
||||||
|
</FormKit>
|
||||||
|
|
||||||
|
<FormKit
|
||||||
|
v-if="selectedMenuItemSource === 'custom'"
|
||||||
v-model="formState.spec.displayName"
|
v-model="formState.spec.displayName"
|
||||||
label="名称"
|
label="名称"
|
||||||
type="text"
|
type="text"
|
||||||
validation="required"
|
validation="required"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
|
v-if="selectedMenuItemSource === 'custom'"
|
||||||
v-model="formState.spec.href"
|
v-model="formState.spec.href"
|
||||||
label="链接地址"
|
label="链接地址"
|
||||||
type="text"
|
type="text"
|
||||||
validation="required"
|
validation="required"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
|
|
||||||
|
<FormKit
|
||||||
|
v-if="selectedMenuItemSource === 'post'"
|
||||||
|
v-model="selectedRef"
|
||||||
|
label="文章"
|
||||||
|
type="select"
|
||||||
|
:options="postMap"
|
||||||
|
validation="required"
|
||||||
|
></FormKit>
|
||||||
|
|
||||||
|
<FormKit
|
||||||
|
v-if="selectedMenuItemSource === 'tag'"
|
||||||
|
v-model="selectedRef"
|
||||||
|
label="标签"
|
||||||
|
type="select"
|
||||||
|
:options="tagMap"
|
||||||
|
validation="required"
|
||||||
|
></FormKit>
|
||||||
|
|
||||||
|
<FormKit
|
||||||
|
v-if="selectedMenuItemSource === 'category'"
|
||||||
|
v-model="selectedRef"
|
||||||
|
label="分类"
|
||||||
|
type="select"
|
||||||
|
:options="categoryMap"
|
||||||
|
validation="required"
|
||||||
|
></FormKit>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { IconList, IconSettings, VButton, VSpace } from "@halo-dev/components";
|
import {
|
||||||
|
IconList,
|
||||||
|
IconSettings,
|
||||||
|
VButton,
|
||||||
|
VSpace,
|
||||||
|
VTag,
|
||||||
|
} from "@halo-dev/components";
|
||||||
import Draggable from "vuedraggable";
|
import Draggable from "vuedraggable";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import type { MenuTreeItem } from "@/modules/interface/menus/utils";
|
import type { MenuTreeItem } from "@/modules/interface/menus/utils";
|
||||||
|
@ -32,6 +38,19 @@ function onOpenEditingModal(menuItem: MenuTreeItem) {
|
||||||
function onDelete(menuItem: MenuTreeItem) {
|
function onDelete(menuItem: MenuTreeItem) {
|
||||||
emit("delete", menuItem);
|
emit("delete", menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
|
||||||
|
if (menuItem.spec.postRef) {
|
||||||
|
return "文章";
|
||||||
|
}
|
||||||
|
if (menuItem.spec.categoryRef) {
|
||||||
|
return "分类";
|
||||||
|
}
|
||||||
|
if (menuItem.spec.tagRef) {
|
||||||
|
return "标签";
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<draggable
|
<draggable
|
||||||
|
@ -52,26 +71,29 @@ function onDelete(menuItem: MenuTreeItem) {
|
||||||
class="group relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
|
class="group relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="drag-element absolute inset-y-0 left-0 flex hidden w-3.5 cursor-move items-center bg-gray-100 transition-all hover:bg-gray-200 group-hover:flex"
|
class="drag-element absolute inset-y-0 left-0 hidden w-3.5 cursor-move items-center bg-gray-100 transition-all hover:bg-gray-200 group-hover:flex"
|
||||||
>
|
>
|
||||||
<IconList class="h-3.5 w-3.5" />
|
<IconList class="h-3.5 w-3.5" />
|
||||||
</div>
|
</div>
|
||||||
<div class="relative flex flex-row items-center">
|
<div class="relative flex flex-row items-center">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="flex flex-row items-center">
|
<div class="flex flex-row items-center gap-2">
|
||||||
<span class="truncate text-sm font-medium text-gray-900">
|
<span class="truncate text-sm font-medium text-gray-900">
|
||||||
{{ menuItem.spec.displayName }}
|
{{ menuItem.status.displayName }}
|
||||||
</span>
|
</span>
|
||||||
|
<VTag v-if="getMenuItemRefDisplayName(menuItem)">
|
||||||
|
{{ getMenuItemRefDisplayName(menuItem) }}
|
||||||
|
</VTag>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-1 flex">
|
<div class="mt-1 flex">
|
||||||
<VSpace align="start" direction="column" spacing="xs">
|
<VSpace align="start" direction="column" spacing="xs">
|
||||||
<a
|
<a
|
||||||
:href="menuItem.spec.href"
|
:href="menuItem.status.href"
|
||||||
class="text-xs text-gray-500 hover:text-gray-900"
|
class="text-xs text-gray-500 hover:text-gray-900"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
{{ menuItem.spec.href }}
|
{{ menuItem.status.href }}
|
||||||
</a>
|
</a>
|
||||||
</VSpace>
|
</VSpace>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue