mirror of https://github.com/halo-dev/halo
feat: add slug existence check when creating posts (#7617)
#### What type of PR is this? /area ui /kind improvement /milestone 2.21.x #### What this PR does / why we need it: This PR adds frontend support for checking whether an slug already exists when creating posts. Note: 1. The current implementation isn’t perfect, some actions (like clicking the “Publish” button) don’t check for duplicate slug yet. 2. Slug checking in the user center might not be accurate, since it may not have permission to query all posts. <img width="695" alt="image" src="https://github.com/user-attachments/assets/baa37a82-49c2-43be-a4d8-0e0f22a9d73b" /> #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/7615 Fixes https://github.com/halo-dev/halo/issues/3332 #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note 创建文章时支持检查别名是否重复 ```pull/7642/head
parent
cc3b3323c7
commit
2cf0d6853a
|
@ -6,7 +6,7 @@ import { toDatetimeLocal, toISOString } from "@/utils/date";
|
||||||
import { randomUUID } from "@/utils/id";
|
import { randomUUID } from "@/utils/id";
|
||||||
import useSlugify from "@console/composables/use-slugify";
|
import useSlugify from "@console/composables/use-slugify";
|
||||||
import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme";
|
import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme";
|
||||||
import { submitForm } from "@formkit/core";
|
import { submitForm, type FormKitNode } from "@formkit/core";
|
||||||
import type { SinglePage } from "@halo-dev/api-client";
|
import type { SinglePage } from "@halo-dev/api-client";
|
||||||
import { coreApiClient } from "@halo-dev/api-client";
|
import { coreApiClient } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
|
@ -266,6 +266,28 @@ const { handleGenerateSlug } = useSlugify(
|
||||||
computed(() => !isUpdateMode),
|
computed(() => !isUpdateMode),
|
||||||
FormType.SINGLE_PAGE
|
FormType.SINGLE_PAGE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// fixme: check if slug is unique
|
||||||
|
// Finally, we need to check if the slug is unique in the database
|
||||||
|
async function slugUniqueValidation(node: FormKitNode) {
|
||||||
|
const value = node.value;
|
||||||
|
if (!value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldSelector = [`spec.slug=${value}`];
|
||||||
|
|
||||||
|
if (isUpdateMode) {
|
||||||
|
fieldSelector.push(`metadata.name!=${formState.value.metadata.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: pagesWithSameSlug } =
|
||||||
|
await coreApiClient.content.singlePage.listSinglePage({
|
||||||
|
fieldSelector,
|
||||||
|
});
|
||||||
|
|
||||||
|
return !pagesWithSameSlug.total;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -309,7 +331,13 @@ const { handleGenerateSlug } = useSlugify(
|
||||||
:label="$t('core.page.settings.fields.slug.label')"
|
:label="$t('core.page.settings.fields.slug.label')"
|
||||||
name="slug"
|
name="slug"
|
||||||
type="text"
|
type="text"
|
||||||
validation="required|length:0,100"
|
validation="required|length:0,100|slugUniqueValidation"
|
||||||
|
:validation-rules="{ slugUniqueValidation }"
|
||||||
|
:validation-messages="{
|
||||||
|
slugUniqueValidation: $t(
|
||||||
|
'core.common.form.validation.slug_unique'
|
||||||
|
),
|
||||||
|
}"
|
||||||
:help="$t('core.page.settings.fields.slug.help')"
|
:help="$t('core.page.settings.fields.slug.help')"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
|
|
|
@ -34,6 +34,7 @@ import type { EditorProvider } from "@halo-dev/console-shared";
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import type { AxiosRequestConfig } from "axios";
|
import type { AxiosRequestConfig } from "axios";
|
||||||
|
import ShortUniqueId from "short-unique-id";
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
nextTick,
|
nextTick,
|
||||||
|
@ -49,6 +50,8 @@ import { useRouter } from "vue-router";
|
||||||
import PostSettingModal from "./components/PostSettingModal.vue";
|
import PostSettingModal from "./components/PostSettingModal.vue";
|
||||||
import { usePostUpdateMutate } from "./composables/use-post-update-mutate";
|
import { usePostUpdateMutate } from "./composables/use-post-update-mutate";
|
||||||
|
|
||||||
|
const uid = new ShortUniqueId();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { mutateAsync: postUpdateMutate } = usePostUpdateMutate();
|
const { mutateAsync: postUpdateMutate } = usePostUpdateMutate();
|
||||||
|
@ -151,6 +154,21 @@ provide<ComputedRef<string | undefined>>(
|
||||||
computed(() => formState.value.post.status?.permalink)
|
computed(() => formState.value.post.status?.permalink)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Slug generation
|
||||||
|
const { handleGenerateSlug } = useSlugify(
|
||||||
|
computed(() => formState.value.post.spec.title),
|
||||||
|
computed({
|
||||||
|
get() {
|
||||||
|
return formState.value.post.spec.slug;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
formState.value.post.spec.slug = value;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
computed(() => !isUpdateMode.value),
|
||||||
|
FormType.POST
|
||||||
|
);
|
||||||
|
|
||||||
const handleSave = async (options?: { mute?: boolean }) => {
|
const handleSave = async (options?: { mute?: boolean }) => {
|
||||||
try {
|
try {
|
||||||
if (!options?.mute) {
|
if (!options?.mute) {
|
||||||
|
@ -162,10 +180,6 @@ const handleSave = async (options?: { mute?: boolean }) => {
|
||||||
formState.value.post.spec.title = t("core.post_editor.untitled");
|
formState.value.post.spec.title = t("core.post_editor.untitled");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!formState.value.post.spec.slug) {
|
|
||||||
formState.value.post.spec.slug = new Date().getTime().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUpdateMode.value) {
|
if (isUpdateMode.value) {
|
||||||
// Save post title
|
// Save post title
|
||||||
if (isTitleChanged.value) {
|
if (isTitleChanged.value) {
|
||||||
|
@ -186,6 +200,21 @@ const handleSave = async (options?: { mute?: boolean }) => {
|
||||||
// Clear new post content cache
|
// Clear new post content cache
|
||||||
handleClearCache();
|
handleClearCache();
|
||||||
|
|
||||||
|
if (!formState.value.post.spec.slug) {
|
||||||
|
handleGenerateSlug(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixme: check if slug is unique
|
||||||
|
// Finally, we need to check if the slug is unique in the database
|
||||||
|
const { data: postsWithSameSlug } =
|
||||||
|
await coreApiClient.content.post.listPost({
|
||||||
|
fieldSelector: [`spec.slug=${formState.value.post.spec.slug}`],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (postsWithSameSlug.total) {
|
||||||
|
formState.value.post.spec.slug = `${formState.value.post.spec.slug}-${uid.randomUUID(8)}`;
|
||||||
|
}
|
||||||
|
|
||||||
const { data } = await consoleApiClient.content.post.draftPost({
|
const { data } = await consoleApiClient.content.post.draftPost({
|
||||||
postRequest: formState.value,
|
postRequest: formState.value,
|
||||||
});
|
});
|
||||||
|
@ -465,21 +494,6 @@ async function handleUploadImage(file: File, options?: AxiosRequestConfig) {
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slug generation
|
|
||||||
useSlugify(
|
|
||||||
computed(() => formState.value.post.spec.title),
|
|
||||||
computed({
|
|
||||||
get() {
|
|
||||||
return formState.value.post.spec.slug;
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
formState.value.post.spec.slug = value;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
computed(() => !isUpdateMode.value),
|
|
||||||
FormType.POST
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { formatDatetime, toDatetimeLocal, toISOString } from "@/utils/date";
|
||||||
import { randomUUID } from "@/utils/id";
|
import { randomUUID } from "@/utils/id";
|
||||||
import useSlugify from "@console/composables/use-slugify";
|
import useSlugify from "@console/composables/use-slugify";
|
||||||
import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme";
|
import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme";
|
||||||
import { submitForm } from "@formkit/core";
|
import { submitForm, type FormKitNode } from "@formkit/core";
|
||||||
import type { Post } from "@halo-dev/api-client";
|
import type { Post } from "@halo-dev/api-client";
|
||||||
import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
|
import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
|
@ -258,6 +258,29 @@ const { handleGenerateSlug } = useSlugify(
|
||||||
FormType.POST
|
FormType.POST
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// fixme: check if slug is unique
|
||||||
|
// Finally, we need to check if the slug is unique in the database
|
||||||
|
async function slugUniqueValidation(node: FormKitNode) {
|
||||||
|
const value = node.value;
|
||||||
|
if (!value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldSelector = [`spec.slug=${value}`];
|
||||||
|
|
||||||
|
if (isUpdateMode.value) {
|
||||||
|
fieldSelector.push(`metadata.name!=${formState.value.metadata.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: postsWithSameSlug } = await coreApiClient.content.post.listPost(
|
||||||
|
{
|
||||||
|
fieldSelector,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return !postsWithSameSlug.total;
|
||||||
|
}
|
||||||
|
|
||||||
// Buttons condition
|
// Buttons condition
|
||||||
const showPublishButton = computed(() => {
|
const showPublishButton = computed(() => {
|
||||||
if (!props.publishSupport) {
|
if (!props.publishSupport) {
|
||||||
|
@ -322,7 +345,13 @@ const showCancelPublishButton = computed(() => {
|
||||||
:label="$t('core.post.settings.fields.slug.label')"
|
:label="$t('core.post.settings.fields.slug.label')"
|
||||||
name="slug"
|
name="slug"
|
||||||
type="text"
|
type="text"
|
||||||
validation="required|length:0,100"
|
validation="required|length:0,100|slugUniqueValidation"
|
||||||
|
:validation-rules="{ slugUniqueValidation }"
|
||||||
|
:validation-messages="{
|
||||||
|
slugUniqueValidation: $t(
|
||||||
|
'core.common.form.validation.slug_unique'
|
||||||
|
),
|
||||||
|
}"
|
||||||
:help="$t('core.post.settings.fields.slug.help')"
|
:help="$t('core.post.settings.fields.slug.help')"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { usePermission } from "@/utils/permission";
|
||||||
import { useSaveKeybinding } from "@console/composables/use-save-keybinding";
|
import { useSaveKeybinding } from "@console/composables/use-save-keybinding";
|
||||||
import useSlugify from "@console/composables/use-slugify";
|
import useSlugify from "@console/composables/use-slugify";
|
||||||
import type { Content, Post, Snapshot } from "@halo-dev/api-client";
|
import type { Content, Post, Snapshot } from "@halo-dev/api-client";
|
||||||
import { ucApiClient } from "@halo-dev/api-client";
|
import { publicApiClient, ucApiClient } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
IconBookRead,
|
IconBookRead,
|
||||||
|
@ -29,6 +29,7 @@ import { usePostUpdateMutate } from "@uc/modules/contents/posts/composables/use-
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import { AxiosError, type AxiosRequestConfig } from "axios";
|
import { AxiosError, type AxiosRequestConfig } from "axios";
|
||||||
|
import ShortUniqueId from "short-unique-id";
|
||||||
import type { ComputedRef } from "vue";
|
import type { ComputedRef } from "vue";
|
||||||
import { computed, nextTick, onMounted, provide, ref, toRef, watch } from "vue";
|
import { computed, nextTick, onMounted, provide, ref, toRef, watch } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
@ -36,6 +37,8 @@ import { useRouter } from "vue-router";
|
||||||
import PostCreationModal from "./components/PostCreationModal.vue";
|
import PostCreationModal from "./components/PostCreationModal.vue";
|
||||||
import PostSettingEditModal from "./components/PostSettingEditModal.vue";
|
import PostSettingEditModal from "./components/PostSettingEditModal.vue";
|
||||||
|
|
||||||
|
const uid = new ShortUniqueId();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { currentUserHasPermission } = usePermission();
|
const { currentUserHasPermission } = usePermission();
|
||||||
|
@ -88,6 +91,10 @@ watch(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isUpdateMode = computed(
|
||||||
|
() => !!formState.value.metadata.creationTimestamp
|
||||||
|
);
|
||||||
|
|
||||||
// provide some data to editor
|
// provide some data to editor
|
||||||
provide<ComputedRef<string | undefined>>(
|
provide<ComputedRef<string | undefined>>(
|
||||||
"owner",
|
"owner",
|
||||||
|
@ -178,6 +185,21 @@ useAutoSaveContent(currentCache, toRef(content.value, "raw"), async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Slug generation
|
||||||
|
const { handleGenerateSlug } = useSlugify(
|
||||||
|
computed(() => formState.value.spec.title),
|
||||||
|
computed({
|
||||||
|
get() {
|
||||||
|
return formState.value.spec.slug;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
formState.value.spec.slug = value;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
computed(() => !isUpdateMode.value),
|
||||||
|
FormType.POST
|
||||||
|
);
|
||||||
|
|
||||||
async function getLatestPost() {
|
async function getLatestPost() {
|
||||||
if (!name.value) {
|
if (!name.value) {
|
||||||
return;
|
return;
|
||||||
|
@ -283,8 +305,20 @@ async function handleCreate() {
|
||||||
if (!formState.value.spec.title) {
|
if (!formState.value.spec.title) {
|
||||||
formState.value.spec.title = t("core.post_editor.untitled");
|
formState.value.spec.title = t("core.post_editor.untitled");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!formState.value.spec.slug) {
|
if (!formState.value.spec.slug) {
|
||||||
formState.value.spec.slug = new Date().getTime().toString();
|
handleGenerateSlug(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixme: check if slug is unique
|
||||||
|
// Finally, we need to check if the slug is unique in the database
|
||||||
|
const { data: postsWithSameSlug } =
|
||||||
|
await publicApiClient.content.post.queryPosts({
|
||||||
|
fieldSelector: [`spec.slug=${formState.value.spec.slug}`],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (postsWithSameSlug.total) {
|
||||||
|
formState.value.spec.slug = `${formState.value.spec.slug}-${uid.randomUUID(8)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data: createdPost } = await ucApiClient.content.post.createMyPost({
|
const { data: createdPost } = await ucApiClient.content.post.createMyPost({
|
||||||
|
@ -303,9 +337,6 @@ async function onCreatePostSuccess(data: Post) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save post
|
// Save post
|
||||||
const isUpdateMode = computed(
|
|
||||||
() => !!formState.value.metadata.creationTimestamp
|
|
||||||
);
|
|
||||||
|
|
||||||
const { mutateAsync: postUpdateMutate } = usePostUpdateMutate();
|
const { mutateAsync: postUpdateMutate } = usePostUpdateMutate();
|
||||||
|
|
||||||
|
@ -446,21 +477,6 @@ async function handleUploadImage(file: File, options?: AxiosRequestConfig) {
|
||||||
|
|
||||||
// Keep session alive
|
// Keep session alive
|
||||||
useSessionKeepAlive();
|
useSessionKeepAlive();
|
||||||
|
|
||||||
// Slug generation
|
|
||||||
useSlugify(
|
|
||||||
computed(() => formState.value.spec.title),
|
|
||||||
computed({
|
|
||||||
get() {
|
|
||||||
return formState.value.spec.slug;
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
formState.value.spec.slug = value;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
computed(() => !isUpdateMode.value),
|
|
||||||
FormType.POST
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -65,20 +65,21 @@ async function onSubmit(data: PostFormState) {
|
||||||
>
|
>
|
||||||
<PostSettingForm
|
<PostSettingForm
|
||||||
:form-state="{
|
:form-state="{
|
||||||
title: props.post.spec.title,
|
title: post.spec.title,
|
||||||
slug: props.post.spec.slug,
|
slug: post.spec.slug,
|
||||||
cover: props.post.spec.cover,
|
cover: post.spec.cover,
|
||||||
categories: props.post.spec.categories,
|
categories: post.spec.categories,
|
||||||
tags: props.post.spec.tags,
|
tags: post.spec.tags,
|
||||||
allowComment: props.post.spec.allowComment,
|
allowComment: post.spec.allowComment,
|
||||||
visible: props.post.spec.visible,
|
visible: post.spec.visible,
|
||||||
pinned: props.post.spec.pinned,
|
pinned: post.spec.pinned,
|
||||||
publishTime: props.post.spec.publishTime
|
publishTime: post.spec.publishTime
|
||||||
? toDatetimeLocal(props.post.spec.publishTime)
|
? toDatetimeLocal(post.spec.publishTime)
|
||||||
: undefined,
|
: undefined,
|
||||||
excerptAutoGenerate: props.post.spec.excerpt.autoGenerate,
|
excerptAutoGenerate: post.spec.excerpt.autoGenerate,
|
||||||
excerptRaw: props.post.spec.excerpt.raw,
|
excerptRaw: post.spec.excerpt.raw,
|
||||||
}"
|
}"
|
||||||
|
:name="post.metadata.name"
|
||||||
update-mode
|
update-mode
|
||||||
@submit="onSubmit"
|
@submit="onSubmit"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -3,6 +3,8 @@ import HasPermission from "@/components/permission/HasPermission.vue";
|
||||||
import { FormType } from "@/types/slug";
|
import { FormType } from "@/types/slug";
|
||||||
import { formatDatetime, toISOString } from "@/utils/date";
|
import { formatDatetime, toISOString } from "@/utils/date";
|
||||||
import useSlugify from "@console/composables/use-slugify";
|
import useSlugify from "@console/composables/use-slugify";
|
||||||
|
import type { FormKitNode } from "@formkit/core";
|
||||||
|
import { publicApiClient } from "@halo-dev/api-client";
|
||||||
import { IconRefreshLine } from "@halo-dev/components";
|
import { IconRefreshLine } from "@halo-dev/components";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
@ -12,10 +14,12 @@ const { t } = useI18n();
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
name?: string;
|
||||||
formState?: PostFormState;
|
formState?: PostFormState;
|
||||||
updateMode?: boolean;
|
updateMode?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
|
name: undefined,
|
||||||
formState: undefined,
|
formState: undefined,
|
||||||
updateMode: false,
|
updateMode: false,
|
||||||
}
|
}
|
||||||
|
@ -63,6 +67,28 @@ const { handleGenerateSlug } = useSlugify(
|
||||||
FormType.POST
|
FormType.POST
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// fixme: check if slug is unique
|
||||||
|
// Finally, we need to check if the slug is unique in the database
|
||||||
|
async function slugUniqueValidation(node: FormKitNode) {
|
||||||
|
const value = node.value;
|
||||||
|
if (!value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldSelector = [`spec.slug=${value}`];
|
||||||
|
|
||||||
|
if (props.name) {
|
||||||
|
fieldSelector.push(`metadata.name!=${props.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: postsWithSameSlug } =
|
||||||
|
await publicApiClient.content.post.queryPosts({
|
||||||
|
fieldSelector,
|
||||||
|
});
|
||||||
|
|
||||||
|
return !postsWithSameSlug.total;
|
||||||
|
}
|
||||||
|
|
||||||
const isScheduledPublish = computed(() => {
|
const isScheduledPublish = computed(() => {
|
||||||
const { publishTime } = internalFormState.value;
|
const { publishTime } = internalFormState.value;
|
||||||
return publishTime && new Date(publishTime) > new Date();
|
return publishTime && new Date(publishTime) > new Date();
|
||||||
|
@ -107,7 +133,13 @@ const publishTimeHelp = computed(() => {
|
||||||
:label="$t('core.post.settings.fields.slug.label')"
|
:label="$t('core.post.settings.fields.slug.label')"
|
||||||
name="slug"
|
name="slug"
|
||||||
type="text"
|
type="text"
|
||||||
validation="required|length:0,100"
|
validation="required|length:0,100|slugUniqueValidation"
|
||||||
|
:validation-rules="{ slugUniqueValidation }"
|
||||||
|
:validation-messages="{
|
||||||
|
slugUniqueValidation: $t(
|
||||||
|
'core.common.form.validation.slug_unique'
|
||||||
|
),
|
||||||
|
}"
|
||||||
:help="$t('core.post.settings.fields.slug.help')"
|
:help="$t('core.post.settings.fields.slug.help')"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
|
|
Loading…
Reference in New Issue