diff --git a/src/stores/index.ts b/src/stores/index.ts index 033e9c9b6..3de0787e2 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -224,6 +224,13 @@ export const useStore = defineStore(`store`, () => { const readingTime = ref(null) + // 文章标题 + const titleList = ref<{ + url: string + title: string + level: number + }[]>([]) + // 更新编辑器 const editorRefresh = () => { codeThemeChange() @@ -240,6 +247,26 @@ export const useStore = defineStore(`store`, () => { } = renderer.parseFrontMatterAndContent(editor.value!.getValue()) readingTime.value = readingTimeResult let outputTemp = marked.parse(markdownContent) as string + + // 提取标题 + const div = document.createElement('div') + div.innerHTML = outputTemp + const list = div.querySelectorAll(`[data-heading]`) + + titleList.value = [] + let i = 0 + for (const item of list) { + item.setAttribute(`id`, `${i}`) + titleList.value.push({ + url: `#${i}`, + title: `${item.innerText}`, + level: Number(item.tagName.slice(1)) + }) + i++ + } + + outputTemp = div.innerHTML + outputTemp = DOMPurify.sanitize(outputTemp) // 阅读时间及字数统计 @@ -595,6 +622,8 @@ export const useStore = defineStore(`store`, () => { delPost, isOpenPostSlider, isOpenRightSlider, + + titleList, } }) diff --git a/src/utils/renderer.ts b/src/utils/renderer.ts index 0be2d3bbf..cd3a23efa 100644 --- a/src/utils/renderer.ts +++ b/src/utils/renderer.ts @@ -157,7 +157,8 @@ export function initRenderer(opts: IOpts) { function styledContent(styleLabel: string, content: string, tagName?: string): string { const tag = tagName ?? styleLabel - return `<${tag} ${styles(styleLabel)}>${content}` + + return `<${tag} ${/^h\d$/.test(tag) ? `data-heading="true"` : ``} ${styles(styleLabel)}>${content}` } function addFootnote(title: string, link: string): number { diff --git a/src/views/CodemirrorEditor.vue b/src/views/CodemirrorEditor.vue index 02dac4614..020d0b7ad 100644 --- a/src/views/CodemirrorEditor.vue +++ b/src/views/CodemirrorEditor.vue @@ -9,6 +9,7 @@ import { } from '@/utils' import fileApi from '@/utils/file' import CodeMirror from 'codemirror' +import { List } from 'lucide-vue-next' const store = useStore() const displayStore = useDisplayStore() @@ -361,6 +362,8 @@ onMounted(() => { onEditorRefresh() mdLocalToRemote() }) + +const isOpenHeadingSlider = ref(false)