1+ import type { TopicModel } from '@mx-space/api-client'
12import { add } from 'date-fns'
23import { isString } from 'es-toolkit/compat'
4+ import type { LexicalEditor } from 'lexical'
35import {
46 BookmarkIcon ,
57 MapPinIcon ,
@@ -21,13 +23,7 @@ import {
2123} from 'vue'
2224import { useRouter } from 'vue-router'
2325import { toast } from 'vue-sonner'
24- import type { TopicModel } from '@mx-space/api-client'
2526import type { CreateNoteData } from '~/api/notes'
26- import type { DraftModel } from '~/models/draft'
27- import type { Coordinate , NoteModel } from '~/models/note'
28- import type { ContentFormat , WriteBaseType } from '~/shared/types/base'
29- import type { LexicalEditor } from 'lexical'
30-
3127import { notesApi } from '~/api/notes'
3228import { topicsApi } from '~/api/topics'
3329import { AiHelperButton } from '~/components/ai/ai-helper'
@@ -44,6 +40,7 @@ import {
4440 TextBaseDrawer ,
4541} from '~/components/drawer/text-base-drawer'
4642import { WriteEditor } from '~/components/editor/write-editor'
43+ import { SlugInput } from '~/components/editor/write-editor/slug-input'
4744import { GetLocationButton } from '~/components/location/get-location-button'
4845import { SearchLocationButton } from '~/components/location/search-button'
4946import { ParseContentButton } from '~/components/special-button/parse-content'
@@ -55,11 +52,15 @@ import { usePreferredContentFormat } from '~/hooks/use-preferred-content-format'
5552import { useStoreRef } from '~/hooks/use-store-ref'
5653import { useWriteDraft } from '~/hooks/use-write-draft'
5754import { useLayout } from '~/layouts/content'
55+ import type { DraftModel } from '~/models/draft'
5856import { DraftRefType } from '~/models/draft'
57+ import type { Coordinate , NoteModel } from '~/models/note'
58+ import type { ContentFormat , WriteBaseType } from '~/shared/types/base'
5959import { UIStore } from '~/stores/ui'
6060import { getDayOfYear } from '~/utils/time'
6161
6262type NoteReactiveType = {
63+ slug : string
6364 mood : string
6465 weather : string
6566 password : string | null
@@ -84,6 +85,17 @@ const useNoteTopic = () => {
8485 return { topics, fetchTopic }
8586}
8687
88+ const buildNotePublicPath = (
89+ note : Pick < NoteReactiveType , 'slug' | 'created' > & { nid ?: number } ,
90+ ) => {
91+ if ( note . slug ) {
92+ const date = note . created ? new Date ( note . created ) : new Date ( )
93+ return `/notes/${ date . getUTCFullYear ( ) } /${ date . getUTCMonth ( ) + 1 } /${ date . getUTCDate ( ) } /${ note . slug } `
94+ }
95+
96+ return note . nid ? `/notes/${ note . nid } ` : ''
97+ }
98+
8799const NoteWriteView = defineComponent ( ( ) => {
88100 const defaultTitle = ref ( '新建日记' )
89101 const router = useRouter ( )
@@ -97,6 +109,7 @@ const NoteWriteView = defineComponent(() => {
97109 const resetReactive : ( ) => NoteReactiveType = ( ) => ( {
98110 text : '' ,
99111 title : '' ,
112+ slug : '' ,
100113 bookmark : false ,
101114 mood : '' ,
102115 password : null ,
@@ -135,6 +148,7 @@ const NoteWriteView = defineComponent(() => {
135148 target . meta = draft . meta
136149 if ( draft . typeSpecificData ) {
137150 const specific = draft . typeSpecificData
151+ target . slug = specific . slug || ( isPartial ? target . slug : '' )
138152 target . mood = specific . mood || ( isPartial ? target . mood : '' )
139153 target . weather = specific . weather || ( isPartial ? target . weather : '' )
140154 target . password =
@@ -198,6 +212,7 @@ const NoteWriteView = defineComponent(() => {
198212 location : data . location ,
199213 coordinates : data . coordinates ,
200214 topicId : data . topicId ,
215+ slug : data . slug ,
201216 isPublished : data . isPublished ,
202217 } ,
203218 } ) ,
@@ -245,6 +260,7 @@ const NoteWriteView = defineComponent(() => {
245260 return {
246261 ...toRaw ( data ) ,
247262 title : data . title ?. trim ( ) || defaultTitle . value ,
263+ slug : data . slug . trim ( ) || undefined ,
248264 password :
249265 data . password && data . password . length > 0 ? data . password : null ,
250266 publicAt : data . publicAt
@@ -375,7 +391,7 @@ const NoteWriteView = defineComponent(() => {
375391 variant = "note"
376392 subtitleSlot = { ( ) => (
377393 < div class = "flex items-center gap-2 text-sm text-neutral-500" >
378- < span > { `${ WEB_URL } /notes/ ${ nid . value ?? '' } ` } </ span >
394+ < span > { `${ WEB_URL } ${ buildNotePublicPath ( { ... data , nid : nid . value } ) } ` } </ span >
379395 { data . text . length > 0 && < AiHelperButton reactiveData = { data } /> }
380396 </ div >
381397 ) }
@@ -393,6 +409,23 @@ const NoteWriteView = defineComponent(() => {
393409 >
394410 < SectionTitle icon = { BookmarkIcon } > 日记信息</ SectionTitle >
395411
412+ < FormField
413+ label = "Slug"
414+ description = "用于 SEO 路径,留空则使用旧 nid 路径"
415+ >
416+ < SlugInput
417+ prefix = { `${ WEB_URL } /notes/${ ( ( ) => {
418+ const date = data . created ? new Date ( data . created ) : new Date ( )
419+ return `${ date . getUTCFullYear ( ) } /${ date . getUTCMonth ( ) + 1 } /${ date . getUTCDate ( ) } /`
420+ } ) ( ) } `}
421+ value = { data . slug }
422+ onChange = { ( value ) => {
423+ data . slug = value
424+ } }
425+ placeholder = "note-slug"
426+ />
427+ </ FormField >
428+
396429 < div class = "grid grid-cols-2 gap-3" >
397430 < FormField label = "心情" required >
398431 < NSelect
0 commit comments