|
|
@ -1,6 +1,6 @@ |
|
|
|
<template> |
|
|
|
<div id="blogDetail"> |
|
|
|
<div v-for="anchor in titles" :style="{ padding: `10px 0 10px ${anchor.indent * 20}px` }" |
|
|
|
<div v-for="anchor in titles" :style="{ padding: `4px 0 4px ${anchor.indent * 20}px` }" |
|
|
|
@click="handleAnchorClick(anchor)" :key="anchor.lineIndex"> |
|
|
|
<a style="cursor: pointer">{{ anchor.title }}</a> |
|
|
|
</div> |
|
|
@ -9,25 +9,24 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
import { ref, onMounted } from 'vue'; |
|
|
|
import { useRoute } from 'vue-router'; |
|
|
|
import { get } from '@/tools/request'; |
|
|
|
import { ref, onMounted, nextTick } from 'vue'; |
|
|
|
import { useRoute } from 'vue-router'; |
|
|
|
import { get } from '@/tools/request'; |
|
|
|
|
|
|
|
const route = useRoute(); |
|
|
|
const text = ref({ |
|
|
|
const route = useRoute(); |
|
|
|
const text = ref({ |
|
|
|
blogtitle: "", |
|
|
|
blogcontent: "" |
|
|
|
}); |
|
|
|
const titles = ref<{ title: string; lineIndex: string; indent: number }[]>([]); |
|
|
|
const preview = ref<any>(null); |
|
|
|
}); |
|
|
|
const titles = ref<{ title: string; lineIndex: string; indent: number }[]>([]); |
|
|
|
|
|
|
|
// Props for v-md-preview component |
|
|
|
const previewProps = { |
|
|
|
// Props for v-md-preview component |
|
|
|
const previewProps = { |
|
|
|
leftToolbar: "undo redo clear | h bold italic strikethrough quote | ul ol table hr | link image code | save | tip | emoji" |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
// Fetch blog content |
|
|
|
const blogOneList = async () => { |
|
|
|
// Fetch blog content |
|
|
|
const blogOneList = async () => { |
|
|
|
try { |
|
|
|
const response = await get(`/blogs/list/${route.params.id}`); |
|
|
|
if (response.data.data) { |
|
|
@ -36,12 +35,13 @@ |
|
|
|
} catch (error) { |
|
|
|
console.error("Error fetching blog content:", error); |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
// Process headings after blog content is fetched |
|
|
|
const processHeadings = () => { |
|
|
|
// Process headings after blog content is fetched |
|
|
|
const processHeadings = async () => { |
|
|
|
await nextTick(); |
|
|
|
const anchors = document.querySelectorAll('#blogDetail h1, #blogDetail h2, #blogDetail h3, #blogDetail h4, #blogDetail h5, #blogDetail h6'); |
|
|
|
const titlesArray = Array.from(anchors).filter((title) => !!title.innerText.trim()); |
|
|
|
const titlesArray = Array.from(anchors).filter((title:any) => !!title.innerText.trim()); |
|
|
|
|
|
|
|
if (!titlesArray.length) { |
|
|
|
titles.value = []; |
|
|
@ -55,23 +55,35 @@ |
|
|
|
lineIndex: el.getAttribute('data-v-md-line') || '', |
|
|
|
indent: hTags.indexOf(el.tagName), |
|
|
|
})); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
// Handle anchor click |
|
|
|
const handleAnchorClick = (anchor: { title: string; lineIndex: string }) => { |
|
|
|
// Handle anchor click |
|
|
|
const handleAnchorClick = (anchor: { title: string; lineIndex: string }) => { |
|
|
|
const heading = document.querySelector(`#blogDetail [data-v-md-line="${anchor.lineIndex}"]`); |
|
|
|
|
|
|
|
if (heading) { |
|
|
|
heading.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
|
} else { |
|
|
|
// Use MutationObserver to wait for the target element to be available |
|
|
|
const observer = new MutationObserver((mutations, obs) => { |
|
|
|
const heading = document.querySelector(`#blogDetail [data-v-md-line="${anchor.lineIndex}"]`); |
|
|
|
if (heading) { |
|
|
|
heading.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
|
obs.disconnect(); // Stop observing once the target element is found |
|
|
|
} |
|
|
|
}; |
|
|
|
}); |
|
|
|
const blogDetail = document.querySelector('#blogDetail') as HTMLElement; |
|
|
|
observer.observe(blogDetail, { childList: true, subtree: true }); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Lifecycle hook to fetch blog content and process headings |
|
|
|
onMounted(async () => { |
|
|
|
// Lifecycle hook to fetch blog content and process headings |
|
|
|
onMounted(async () => { |
|
|
|
await blogOneList(); |
|
|
|
processHeadings(); |
|
|
|
}); |
|
|
|
</script> |
|
|
|
await processHeadings(); |
|
|
|
}); |
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
|
|
<style scoped> |
|
|
|
#blogDetail { |
|
|
|