mirror of https://github.com/halo-dev/halo-admin
feat: add annotations form for more extension (#801)
#### What type of PR is this? /kind feature #### What this PR does / why we need it: 为更多模型添加 Annotation Form 的支持。此 PR 包括: 1. 自定义页面。 2. 文章分类。 3. 文章标签。 4. 菜单项。 5. 用户。 此 PR 是对 https://github.com/halo-dev/console/pull/770 的补充。 #### Special notes for your reviewer: 测试方式: 1. 将一下内容放到任意一个主题下,后缀为 `yaml`,文件名随意。 ```yaml spec: targetRef: group: content.halo.run kind: Post formSchema: - $formkit: "text" name: "download" label: "下载地址" - $formkit: "text" name: "version" label: "版本" apiVersion: v1alpha1 kind: AnnotationSetting metadata: generateName: annotation- --- spec: targetRef: group: content.halo.run kind: SinglePage formSchema: - $formkit: "text" name: "download" label: "下载地址" - $formkit: "text" name: "version" label: "版本" apiVersion: v1alpha1 kind: AnnotationSetting metadata: generateName: annotation- --- spec: targetRef: group: content.halo.run kind: Category formSchema: - $formkit: "text" name: "download" label: "下载地址" - $formkit: "text" name: "version" label: "版本" apiVersion: v1alpha1 kind: AnnotationSetting metadata: generateName: annotation- --- spec: targetRef: group: content.halo.run kind: Tag formSchema: - $formkit: "text" name: "download" label: "下载地址" - $formkit: "text" name: "version" label: "版本" apiVersion: v1alpha1 kind: AnnotationSetting metadata: generateName: annotation- --- spec: targetRef: group: "" kind: MenuItem formSchema: - $formkit: "text" name: "icon" label: "图标" - $formkit: "text" name: "version" label: "版本" apiVersion: v1alpha1 kind: AnnotationSetting metadata: generateName: annotation- ``` 3. 后端需要使用 https://github.com/halo-dev/halo/pull/3028 4. 测试上述提到的模型的 Annotations 表单。 5. 检查是否可以设置正常。 #### Does this PR introduce a user-facing change? ```release-note None ```pull/802/head
parent
d60931bae5
commit
8d3c289559
|
@ -148,7 +148,11 @@ const specFormInvalid = ref(true);
|
||||||
const customFormInvalid = ref(true);
|
const customFormInvalid = ref(true);
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
submitForm("specForm");
|
if (avaliableAnnotationSettings.value.length) {
|
||||||
|
submitForm("specForm");
|
||||||
|
} else {
|
||||||
|
specFormInvalid.value = false;
|
||||||
|
}
|
||||||
submitForm("customForm");
|
submitForm("customForm");
|
||||||
await nextTick();
|
await nextTick();
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { singlePageLabels } from "@/constants/labels";
|
||||||
import { randomUUID } from "@/utils/id";
|
import { randomUUID } from "@/utils/id";
|
||||||
import { toDatetimeLocal, toISOString } from "@/utils/date";
|
import { toDatetimeLocal, toISOString } from "@/utils/date";
|
||||||
import { submitForm } from "@formkit/core";
|
import { submitForm } from "@formkit/core";
|
||||||
|
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
|
||||||
|
|
||||||
const initialFormState: SinglePage = {
|
const initialFormState: SinglePage = {
|
||||||
spec: {
|
spec: {
|
||||||
|
@ -75,6 +76,8 @@ const onVisibleChange = (visible: boolean) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const annotationsFormRef = ref<InstanceType<typeof AnnotationsForm>>();
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (submitType.value === "publish") {
|
if (submitType.value === "publish") {
|
||||||
handlePublish();
|
handlePublish();
|
||||||
|
@ -101,6 +104,20 @@ const handlePublishClick = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
|
annotationsFormRef.value?.handleSubmit();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
||||||
|
annotationsFormRef.value || {};
|
||||||
|
if (customFormInvalid || specFormInvalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formState.value.metadata.annotations = {
|
||||||
|
...annotations,
|
||||||
|
...customAnnotations,
|
||||||
|
};
|
||||||
|
|
||||||
if (props.onlyEmit) {
|
if (props.onlyEmit) {
|
||||||
emit("saved", formState.value);
|
emit("saved", formState.value);
|
||||||
return;
|
return;
|
||||||
|
@ -138,6 +155,20 @@ const handleSave = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePublish = async () => {
|
const handlePublish = async () => {
|
||||||
|
annotationsFormRef.value?.handleSubmit();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
||||||
|
annotationsFormRef.value || {};
|
||||||
|
if (customFormInvalid || specFormInvalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formState.value.metadata.annotations = {
|
||||||
|
...annotations,
|
||||||
|
...customAnnotations,
|
||||||
|
};
|
||||||
|
|
||||||
if (props.onlyEmit) {
|
if (props.onlyEmit) {
|
||||||
emit("published", formState.value);
|
emit("published", formState.value);
|
||||||
return;
|
return;
|
||||||
|
@ -364,6 +395,27 @@ const onPublishTimeChange = (value: string) => {
|
||||||
</div>
|
</div>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
||||||
|
<div class="py-5">
|
||||||
|
<div class="border-t border-gray-200"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
|
<div class="md:col-span-1">
|
||||||
|
<div class="sticky top-0">
|
||||||
|
<span class="text-base font-medium text-gray-900"> 元数据 </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
|
<AnnotationsForm
|
||||||
|
:key="formState.metadata.name"
|
||||||
|
ref="annotationsFormRef"
|
||||||
|
:value="formState.metadata.annotations"
|
||||||
|
kind="SinglePage"
|
||||||
|
group="content.halo.run"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<template v-if="publishSupport">
|
<template v-if="publishSupport">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// core libs
|
// core libs
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, nextTick, ref, watch } from "vue";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
|
@ -15,6 +15,7 @@ import cloneDeep from "lodash.clonedeep";
|
||||||
import { reset } from "@formkit/core";
|
import { reset } from "@formkit/core";
|
||||||
import { setFocus } from "@/formkit/utils/focus";
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
import { useThemeCustomTemplates } from "@/modules/interface/themes/composables/use-theme";
|
import { useThemeCustomTemplates } from "@/modules/interface/themes/composables/use-theme";
|
||||||
|
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -62,7 +63,23 @@ const modalTitle = computed(() => {
|
||||||
return isUpdateMode.value ? "编辑文章分类" : "新增文章分类";
|
return isUpdateMode.value ? "编辑文章分类" : "新增文章分类";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const annotationsFormRef = ref<InstanceType<typeof AnnotationsForm>>();
|
||||||
|
|
||||||
const handleSaveCategory = async () => {
|
const handleSaveCategory = async () => {
|
||||||
|
annotationsFormRef.value?.handleSubmit();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
||||||
|
annotationsFormRef.value || {};
|
||||||
|
if (customFormInvalid || specFormInvalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formState.value.metadata.annotations = {
|
||||||
|
...annotations,
|
||||||
|
...customAnnotations,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
if (isUpdateMode.value) {
|
if (isUpdateMode.value) {
|
||||||
|
@ -126,56 +143,89 @@ const { templates } = useThemeCustomTemplates("category");
|
||||||
<VModal
|
<VModal
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="600"
|
:width="700"
|
||||||
@update:visible="onVisibleChange"
|
@update:visible="onVisibleChange"
|
||||||
>
|
>
|
||||||
<FormKit
|
<FormKit
|
||||||
id="category-form"
|
id="category-form"
|
||||||
name="category-form"
|
|
||||||
type="form"
|
type="form"
|
||||||
|
name="category-form"
|
||||||
:config="{ validationVisibility: 'submit' }"
|
:config="{ validationVisibility: 'submit' }"
|
||||||
@submit="handleSaveCategory"
|
@submit="handleSaveCategory"
|
||||||
>
|
>
|
||||||
<FormKit
|
<div>
|
||||||
id="displayNameInput"
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
v-model="formState.spec.displayName"
|
<div class="md:col-span-1">
|
||||||
name="displayName"
|
<div class="sticky top-0">
|
||||||
label="名称"
|
<span class="text-base font-medium text-gray-900"> 常规 </span>
|
||||||
type="text"
|
</div>
|
||||||
validation="required|length:0,50"
|
</div>
|
||||||
></FormKit>
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.slug"
|
id="displayNameInput"
|
||||||
help="通常作为分类访问地址标识"
|
v-model="formState.spec.displayName"
|
||||||
name="slug"
|
name="displayName"
|
||||||
label="别名"
|
label="名称"
|
||||||
type="text"
|
type="text"
|
||||||
validation="required|length:0,50"
|
validation="required|length:0,50"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.template"
|
v-model="formState.spec.slug"
|
||||||
:options="templates"
|
help="通常作为分类访问地址标识"
|
||||||
label="自定义模板"
|
name="slug"
|
||||||
type="select"
|
label="别名"
|
||||||
name="template"
|
type="text"
|
||||||
></FormKit>
|
validation="required|length:0,50"
|
||||||
<FormKit
|
></FormKit>
|
||||||
v-model="formState.spec.cover"
|
<FormKit
|
||||||
help="需要主题适配以支持"
|
v-model="formState.spec.template"
|
||||||
name="cover"
|
:options="templates"
|
||||||
label="封面图"
|
label="自定义模板"
|
||||||
type="attachment"
|
type="select"
|
||||||
validation="length:0,1024"
|
name="template"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.description"
|
v-model="formState.spec.cover"
|
||||||
name="description"
|
help="需要主题适配以支持"
|
||||||
help="需要主题适配以支持"
|
name="cover"
|
||||||
label="描述"
|
label="封面图"
|
||||||
type="textarea"
|
type="attachment"
|
||||||
validation="length:0,200"
|
validation="length:0,1024"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
|
<FormKit
|
||||||
|
v-model="formState.spec.description"
|
||||||
|
name="description"
|
||||||
|
help="需要主题适配以支持"
|
||||||
|
label="描述"
|
||||||
|
type="textarea"
|
||||||
|
validation="length:0,200"
|
||||||
|
></FormKit>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
||||||
|
<div class="py-5">
|
||||||
|
<div class="border-t border-gray-200"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
|
<div class="md:col-span-1">
|
||||||
|
<div class="sticky top-0">
|
||||||
|
<span class="text-base font-medium text-gray-900"> 元数据 </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
|
<AnnotationsForm
|
||||||
|
:key="formState.metadata.name"
|
||||||
|
ref="annotationsFormRef"
|
||||||
|
:value="formState.metadata.annotations"
|
||||||
|
kind="Category"
|
||||||
|
group="content.halo.run"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// core libs
|
// core libs
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, nextTick, ref, watch } from "vue";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
|
@ -21,6 +21,7 @@ import type { Tag } from "@halo-dev/api-client";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import { reset } from "@formkit/core";
|
import { reset } from "@formkit/core";
|
||||||
import { setFocus } from "@/formkit/utils/focus";
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
|
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -66,7 +67,23 @@ const modalTitle = computed(() => {
|
||||||
return isUpdateMode.value ? "编辑文章标签" : "新增文章标签";
|
return isUpdateMode.value ? "编辑文章标签" : "新增文章标签";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const annotationsFormRef = ref<InstanceType<typeof AnnotationsForm>>();
|
||||||
|
|
||||||
const handleSaveTag = async () => {
|
const handleSaveTag = async () => {
|
||||||
|
annotationsFormRef.value?.handleSubmit();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
||||||
|
annotationsFormRef.value || {};
|
||||||
|
if (customFormInvalid || specFormInvalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formState.value.metadata.annotations = {
|
||||||
|
...annotations,
|
||||||
|
...customAnnotations,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
if (isUpdateMode.value) {
|
if (isUpdateMode.value) {
|
||||||
|
@ -127,7 +144,7 @@ watch(
|
||||||
<VModal
|
<VModal
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="600"
|
:width="700"
|
||||||
@update:visible="onVisibleChange"
|
@update:visible="onVisibleChange"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
|
@ -138,46 +155,80 @@ watch(
|
||||||
<IconArrowRight />
|
<IconArrowRight />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
id="tag-form"
|
id="tag-form"
|
||||||
|
type="form"
|
||||||
name="tag-form"
|
name="tag-form"
|
||||||
:config="{ validationVisibility: 'submit' }"
|
:config="{ validationVisibility: 'submit' }"
|
||||||
type="form"
|
|
||||||
@submit="handleSaveTag"
|
@submit="handleSaveTag"
|
||||||
>
|
>
|
||||||
<FormKit
|
<div>
|
||||||
id="displayNameInput"
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
v-model="formState.spec.displayName"
|
<div class="md:col-span-1">
|
||||||
name="displayName"
|
<div class="sticky top-0">
|
||||||
label="名称"
|
<span class="text-base font-medium text-gray-900"> 常规 </span>
|
||||||
type="text"
|
</div>
|
||||||
validation="required|length:0,50"
|
</div>
|
||||||
></FormKit>
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.slug"
|
id="displayNameInput"
|
||||||
help="通常作为标签访问地址标识"
|
v-model="formState.spec.displayName"
|
||||||
label="别名"
|
name="displayName"
|
||||||
name="slug"
|
label="名称"
|
||||||
type="text"
|
type="text"
|
||||||
validation="required|length:0,50"
|
validation="required|length:0,50"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.color"
|
v-model="formState.spec.slug"
|
||||||
name="color"
|
help="通常作为标签访问地址标识"
|
||||||
help="需要主题适配以支持"
|
label="别名"
|
||||||
label="颜色"
|
name="slug"
|
||||||
type="color"
|
type="text"
|
||||||
validation="length:0,50"
|
validation="required|length:0,50"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.cover"
|
v-model="formState.spec.color"
|
||||||
name="cover"
|
name="color"
|
||||||
help="需要主题适配以支持"
|
help="需要主题适配以支持"
|
||||||
label="封面图"
|
label="颜色"
|
||||||
type="attachment"
|
type="color"
|
||||||
validation="length:0,1024"
|
validation="length:0,50"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
|
<FormKit
|
||||||
|
v-model="formState.spec.cover"
|
||||||
|
name="cover"
|
||||||
|
help="需要主题适配以支持"
|
||||||
|
label="封面图"
|
||||||
|
type="attachment"
|
||||||
|
validation="length:0,1024"
|
||||||
|
></FormKit>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
||||||
|
<div class="py-5">
|
||||||
|
<div class="border-t border-gray-200"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
|
<div class="md:col-span-1">
|
||||||
|
<div class="sticky top-0">
|
||||||
|
<span class="text-base font-medium text-gray-900"> 元数据 </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
|
<AnnotationsForm
|
||||||
|
:key="formState.metadata.name"
|
||||||
|
ref="annotationsFormRef"
|
||||||
|
:value="formState.metadata.annotations"
|
||||||
|
kind="Tag"
|
||||||
|
group="content.halo.run"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Toast, VButton, VModal, VSpace } from "@halo-dev/components";
|
import { Toast, 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, nextTick, ref, watch } from "vue";
|
||||||
import type { Menu, MenuItem, Ref } from "@halo-dev/api-client";
|
import type { Menu, MenuItem, Ref } from "@halo-dev/api-client";
|
||||||
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 { setFocus } from "@/formkit/utils/focus";
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
|
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -56,7 +57,23 @@ const modalTitle = computed(() => {
|
||||||
return isUpdateMode.value ? "编辑菜单项" : "新增菜单项";
|
return isUpdateMode.value ? "编辑菜单项" : "新增菜单项";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const annotationsFormRef = ref<InstanceType<typeof AnnotationsForm>>();
|
||||||
|
|
||||||
const handleSaveMenuItem = async () => {
|
const handleSaveMenuItem = async () => {
|
||||||
|
annotationsFormRef.value?.handleSubmit();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
||||||
|
annotationsFormRef.value || {};
|
||||||
|
if (customFormInvalid || specFormInvalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formState.value.metadata.annotations = {
|
||||||
|
...annotations,
|
||||||
|
...customAnnotations,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
|
|
||||||
|
@ -243,7 +260,7 @@ const onMenuItemSourceChange = () => {
|
||||||
<template>
|
<template>
|
||||||
<VModal
|
<VModal
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="500"
|
:width="700"
|
||||||
:title="modalTitle"
|
:title="modalTitle"
|
||||||
@update:visible="onVisibleChange"
|
@update:visible="onVisibleChange"
|
||||||
>
|
>
|
||||||
|
@ -255,54 +272,87 @@ const onMenuItemSourceChange = () => {
|
||||||
:config="{ validationVisibility: 'submit' }"
|
:config="{ validationVisibility: 'submit' }"
|
||||||
@submit="handleSaveMenuItem"
|
@submit="handleSaveMenuItem"
|
||||||
>
|
>
|
||||||
<FormKit
|
<div>
|
||||||
v-if="!isUpdateMode && menu && visible"
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
v-model="selectedParentMenuItem"
|
<div class="md:col-span-1">
|
||||||
label="上级菜单项"
|
<div class="sticky top-0">
|
||||||
placeholder="选择上级菜单项"
|
<span class="text-base font-medium text-gray-900"> 常规 </span>
|
||||||
type="menuItemSelect"
|
</div>
|
||||||
:menu-items="menu?.spec.menuItems || []"
|
</div>
|
||||||
/>
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
|
<FormKit
|
||||||
|
v-if="!isUpdateMode && menu && visible"
|
||||||
|
v-model="selectedParentMenuItem"
|
||||||
|
label="上级菜单项"
|
||||||
|
placeholder="选择上级菜单项"
|
||||||
|
type="menuItemSelect"
|
||||||
|
:menu-items="menu?.spec.menuItems || []"
|
||||||
|
/>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="selectedRefKind"
|
v-model="selectedRefKind"
|
||||||
:options="menuItemRefsMap"
|
:options="menuItemRefsMap"
|
||||||
:disabled="isUpdateMode"
|
:disabled="isUpdateMode"
|
||||||
label="类型"
|
label="类型"
|
||||||
type="select"
|
type="select"
|
||||||
@change="onMenuItemSourceChange"
|
@change="onMenuItemSourceChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
v-if="!selectedRefKind"
|
v-if="!selectedRefKind"
|
||||||
id="displayNameInput"
|
id="displayNameInput"
|
||||||
v-model="formState.spec.displayName"
|
v-model="formState.spec.displayName"
|
||||||
label="名称"
|
label="名称"
|
||||||
type="text"
|
type="text"
|
||||||
name="displayName"
|
name="displayName"
|
||||||
validation="required|length:0,100"
|
validation="required|length:0,100"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
v-if="!selectedRefKind"
|
v-if="!selectedRefKind"
|
||||||
v-model="formState.spec.href"
|
v-model="formState.spec.href"
|
||||||
label="链接地址"
|
label="链接地址"
|
||||||
type="text"
|
type="text"
|
||||||
name="href"
|
name="href"
|
||||||
validation="required|length:0,1024"
|
validation="required|length:0,1024"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormKit
|
<FormKit
|
||||||
v-if="selectedRef?.ref"
|
v-if="selectedRef?.ref"
|
||||||
:id="selectedRef.inputType"
|
:id="selectedRef.inputType"
|
||||||
:key="selectedRef.inputType"
|
:key="selectedRef.inputType"
|
||||||
v-model="selectedRefName"
|
v-model="selectedRefName"
|
||||||
:placeholder="`请选择${selectedRef.label}`"
|
:placeholder="`请选择${selectedRef.label}`"
|
||||||
:label="selectedRef.label"
|
:label="selectedRef.label"
|
||||||
:type="selectedRef.inputType"
|
:type="selectedRef.inputType"
|
||||||
validation="required"
|
validation="required"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
||||||
|
<div class="py-5">
|
||||||
|
<div class="border-t border-gray-200"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
|
<div class="md:col-span-1">
|
||||||
|
<div class="sticky top-0">
|
||||||
|
<span class="text-base font-medium text-gray-900"> 元数据 </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
|
<AnnotationsForm
|
||||||
|
:key="formState.metadata.name"
|
||||||
|
ref="annotationsFormRef"
|
||||||
|
:value="formState.metadata.annotations"
|
||||||
|
kind="MenuItem"
|
||||||
|
group=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// core libs
|
// core libs
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, nextTick, ref, watch } from "vue";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
import type { User } from "@halo-dev/api-client";
|
import type { User } from "@halo-dev/api-client";
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import { reset } from "@formkit/core";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
import { setFocus } from "@/formkit/utils/focus";
|
import { setFocus } from "@/formkit/utils/focus";
|
||||||
|
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
@ -94,7 +95,23 @@ const handleResetForm = () => {
|
||||||
reset("user-form");
|
reset("user-form");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const annotationsFormRef = ref<InstanceType<typeof AnnotationsForm>>();
|
||||||
|
|
||||||
const handleCreateUser = async () => {
|
const handleCreateUser = async () => {
|
||||||
|
annotationsFormRef.value?.handleSubmit();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
||||||
|
annotationsFormRef.value || {};
|
||||||
|
if (customFormInvalid || specFormInvalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
formState.value.metadata.annotations = {
|
||||||
|
...annotations,
|
||||||
|
...customAnnotations,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
if (isUpdateMode.value) {
|
if (isUpdateMode.value) {
|
||||||
|
@ -132,52 +149,84 @@ const handleCreateUser = async () => {
|
||||||
type="form"
|
type="form"
|
||||||
@submit="handleCreateUser"
|
@submit="handleCreateUser"
|
||||||
>
|
>
|
||||||
<FormKit
|
<div>
|
||||||
id="userNameInput"
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
v-model="formState.metadata.name"
|
<div class="md:col-span-1">
|
||||||
:disabled="isUpdateMode"
|
<div class="sticky top-0">
|
||||||
label="用户名"
|
<span class="text-base font-medium text-gray-900"> 常规 </span>
|
||||||
type="text"
|
</div>
|
||||||
name="name"
|
</div>
|
||||||
validation="required|alphanumeric|length:0,50"
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
></FormKit>
|
<FormKit
|
||||||
<FormKit
|
id="userNameInput"
|
||||||
id="displayNameInput"
|
v-model="formState.metadata.name"
|
||||||
v-model="formState.spec.displayName"
|
:disabled="isUpdateMode"
|
||||||
label="显示名称"
|
label="用户名"
|
||||||
type="text"
|
type="text"
|
||||||
name="displayName"
|
name="name"
|
||||||
validation="required|length:0,50"
|
validation="required|alphanumeric|length:0,50"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.email"
|
id="displayNameInput"
|
||||||
label="电子邮箱"
|
v-model="formState.spec.displayName"
|
||||||
type="email"
|
label="显示名称"
|
||||||
name="email"
|
type="text"
|
||||||
validation="required|email|length:0,100"
|
name="displayName"
|
||||||
></FormKit>
|
validation="required|length:0,50"
|
||||||
<FormKit
|
></FormKit>
|
||||||
v-model="formState.spec.phone"
|
<FormKit
|
||||||
label="手机号"
|
v-model="formState.spec.email"
|
||||||
type="text"
|
label="电子邮箱"
|
||||||
name="phone"
|
type="email"
|
||||||
validation="length:0,20"
|
name="email"
|
||||||
></FormKit>
|
validation="required|email|length:0,100"
|
||||||
<FormKit
|
></FormKit>
|
||||||
v-model="formState.spec.avatar"
|
<FormKit
|
||||||
label="头像"
|
v-model="formState.spec.phone"
|
||||||
type="attachment"
|
label="手机号"
|
||||||
name="avatar"
|
type="text"
|
||||||
validation="url|length:0,1024"
|
name="phone"
|
||||||
></FormKit>
|
validation="length:0,20"
|
||||||
<FormKit
|
></FormKit>
|
||||||
v-model="formState.spec.bio"
|
<FormKit
|
||||||
label="描述"
|
v-model="formState.spec.avatar"
|
||||||
type="textarea"
|
label="头像"
|
||||||
name="bio"
|
type="attachment"
|
||||||
validation="length:0,2048"
|
name="avatar"
|
||||||
></FormKit>
|
validation="url|length:0,1024"
|
||||||
|
></FormKit>
|
||||||
|
<FormKit
|
||||||
|
v-model="formState.spec.bio"
|
||||||
|
label="描述"
|
||||||
|
type="textarea"
|
||||||
|
name="bio"
|
||||||
|
validation="length:0,2048"
|
||||||
|
></FormKit>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
||||||
|
<div class="py-5">
|
||||||
|
<div class="border-t border-gray-200"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md:grid md:grid-cols-4 md:gap-6">
|
||||||
|
<div class="md:col-span-1">
|
||||||
|
<div class="sticky top-0">
|
||||||
|
<span class="text-base font-medium text-gray-900"> 元数据 </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0">
|
||||||
|
<AnnotationsForm
|
||||||
|
:key="formState.metadata.name"
|
||||||
|
ref="annotationsFormRef"
|
||||||
|
:value="formState.metadata.annotations"
|
||||||
|
kind="User"
|
||||||
|
group=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
|
Loading…
Reference in New Issue