Browse Source

add new

master
sunfree 8 months ago
parent
commit
2e694a4e17
  1. 100
      src/views/blog/ContentDetail.vue
  2. 97
      src/views/blog/blogcontent/BlogDetailView.vue
  3. 97
      src/views/blog/diarycontent/DiaryDetailView.vue

100
src/views/blog/ContentDetail.vue

@ -0,0 +1,100 @@
<template>
<div :id="containerId">
<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>
<a-card>
<v-md-preview v-if="text.content" :text="text.content" v-bind="previewProps" />
</a-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
import { useRoute } from 'vue-router';
import { get } from '@/tools/request';
const route = useRoute();
const props = defineProps<{
apiUrl: string;
contentField: string;
containerId: string;
}>();
const text = ref({ content: "" });
const titles = ref<{ title: string; lineIndex: string; indent: number }[]>([]);
// 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 content
const fetchContent = async () => {
try {
const response = await get(`${props.apiUrl}/${route.params.id}`);
if (response.data.data) {
text.value.content = response.data.data[props.contentField];
}
} catch (error) {
console.error("Error fetching content:", error);
}
};
// Process headings after content is fetched
const processHeadings = async () => {
await nextTick();
const anchors = document.querySelectorAll(`#${props.containerId} h1, #${props.containerId} h2, #${props.containerId} h3, #${props.containerId} h4, #${props.containerId} h5, #${props.containerId} h6`);
const titlesArray = Array.from(anchors).filter((title: any) => !!title.innerText.trim());
if (!titlesArray.length) {
titles.value = [];
return;
}
const hTags = Array.from(new Set(titlesArray.map((title: any) => title.tagName))).sort();
titles.value = titlesArray.map((el: any) => ({
title: el.innerText,
lineIndex: el.getAttribute('data-v-md-line') || '',
indent: hTags.indexOf(el.tagName),
}));
};
// Handle anchor click
const handleAnchorClick = (anchor: { title: string; lineIndex: string }) => {
const heading = document.querySelector(`#${props.containerId} [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(`#${props.containerId} [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 container = document.querySelector(`#${props.containerId}`) as HTMLElement;
observer.observe(container, { childList: true, subtree: true });
}
};
// Lifecycle hook to fetch content and process headings
onMounted(async () => {
await fetchContent();
await processHeadings();
});
</script>
<style scoped>
#blogDetail, #diaryDetail {
width: 45%;
margin: 0 24px;
}
</style>

97
src/views/blog/blogcontent/BlogDetailView.vue

@ -1,100 +1,7 @@
<template>
<div id="blogDetail">
<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>
<a-card>
<v-md-preview v-if="text.blogcontent" :text="text.blogcontent" v-bind="previewProps" />
</a-card>
</div>
<ContentDetail apiUrl="/blogs/list" contentField="blogcontent" containerId="blogDetail" />
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
import { useRoute } from 'vue-router';
import { get } from '@/tools/request';
const route = useRoute();
const text = ref({
blogtitle: "",
blogcontent: ""
});
const titles = ref<{ title: string; lineIndex: string; indent: number }[]>([]);
// 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 () => {
try {
const response = await get(`/blogs/list/${route.params.id}`);
if (response.data.data) {
text.value.blogcontent = response.data.data.blogcontent;
}
} catch (error) {
console.error("Error fetching blog content:", error);
}
};
// 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: any) => !!title.innerText.trim());
if (!titlesArray.length) {
titles.value = [];
return;
}
const hTags = Array.from(new Set(titlesArray.map((title: any) => title.tagName))).sort();
titles.value = titlesArray.map((el: any) => ({
title: el.innerText,
lineIndex: el.getAttribute('data-v-md-line') || '',
indent: hTags.indexOf(el.tagName),
}));
};
// 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 () => {
await blogOneList();
await processHeadings();
});
import ContentDetail from '@/views/blog/ContentDetail.vue';
</script>
<style scoped>
#blogDetail {
width: 45%;
margin: 0 24px;
}
</style>

97
src/views/blog/diarycontent/DiaryDetailView.vue

@ -1,100 +1,7 @@
<template>
<div id="diaryDetail">
<!-- <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> -->
<a-card>
<v-md-preview v-if="text.diarycontent" :text="text.diarycontent" v-bind="previewProps" />
</a-card>
</div>
<ContentDetail apiUrl="/diarys/list" contentField="diarycontent" containerId="diaryDetail" />
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
import { useRoute } from 'vue-router';
import { get } from '@/tools/request';
const route = useRoute();
const text = ref({
diarytitle: "",
diarycontent: ""
});
const titles = ref<{ title: string; lineIndex: string; indent: number }[]>([]);
// 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 diary content
const diaryOneList = async () => {
try {
const response = await get(`/diarys/list/${route.params.id}`);
if (response.data.data) {
text.value.diarycontent = response.data.data.diarycontent;
}
} catch (error) {
console.error("Error fetching diary content:", error);
}
};
// Process headings after diary content is fetched
const processHeadings = async () => {
await nextTick();
const anchors = document.querySelectorAll('#diaryDetail h1, #diaryDetail h2, #diaryDetail h3, #diaryDetail h4, #diaryDetail h5, #diaryDetail h6');
const titlesArray = Array.from(anchors).filter((title: any) => !!title.innerText.trim());
if (!titlesArray.length) {
titles.value = [];
return;
}
const hTags = Array.from(new Set(titlesArray.map((title: any) => title.tagName))).sort();
titles.value = titlesArray.map((el: any) => ({
title: el.innerText,
lineIndex: el.getAttribute('data-v-md-line') || '',
indent: hTags.indexOf(el.tagName),
}));
};
// Handle anchor click
const handleAnchorClick = (anchor: { title: string; lineIndex: string }) => {
const heading = document.querySelector(`#diaryDetail [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(`#diaryDetail [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 diaryDetail = document.querySelector('#diaryDetail') as HTMLElement;
observer.observe(diaryDetail, { childList: true, subtree: true });
}
};
// Lifecycle hook to fetch diary content and process headings
onMounted(async () => {
await diaryOneList();
await processHeadings();
});
import ContentDetail from '@/views/blog/ContentDetail.vue';
</script>
<style scoped>
#diaryDetail {
width: 45%;
margin: 0 24px;
}
</style>
Loading…
Cancel
Save