123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- <template>
- <div ref="scrollContainer" class="nana-rich-html-container">
- <div class="rich-html">
- <slot name="prepend" />
- <template
- v-for="(content, i) in contents"
- :key="i"
- >
- <div
- v-if="content"
- :data-r-id="id"
- class="content"
- v-html="content"
- />
- </template>
- <slot name="append" />
- </div>
- <div class="rich-html-catalog" v-if="catalog && catalogItems.length > 0">
- <CommonCatalog
- :items="catalogItems"
- :scrollContainer="scrollContainer"
- />
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import CommonUtils from '@/common/utils/CommonUtils';
- import CommonCatalog, { type CatalogItem } from '../content/CommonCatalog.vue';
- import { onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue';
- const props = defineProps({
- contents: {
- type: Array as PropType<string[]>,
- default: () => ([]),
- },
- tagStyle: {
- type: Object as PropType<Record<string, string>>,
- default: () => ({}),
- },
- catalog: {
- type: Boolean,
- default: true,
- },
- noStyle: {
- type: Boolean,
- default: false,
- },
- })
- const id = CommonUtils.genNonDuplicateIDHEX(12);
- const catalogItems = ref<CatalogItem[]>([]);
- const scrollContainer = ref<HTMLElement|null>(null);
- let lastStyleTag : HTMLElement|null = null;
- function genTagCss() {
- if (Object.keys(props.tagStyle).length > 0) {
- const style = document.createElement('style');
- let css = '';
- for (const key in props.tagStyle) {
- css += `.rich-html div[data-r-id="${id}"] ${key} { ${props.tagStyle[key]} } `
- }
- style.innerHTML = css;
- document.body.appendChild(style);
- lastStyleTag = style;
- }
- }
- function generateCatalog() {
- catalogItems.value = [];
- if (!props.catalog)
- return;
- let anchrId = 0;
- for (let i = 1; i <= 6; i++) {
- const heades = document.querySelectorAll(`.rich-html div[data-r-id="${id}"] h${i}`);
- for (const header of heades) {
- anchrId++;
- if (header instanceof HTMLHeadingElement) {
- if (header.id == '')
- header.id = 'header' + anchrId + 'a' + CommonUtils.genNonDuplicateIDHEX(12);
- catalogItems.value.push({
- title: header.textContent || '',
- scrollPos: header.offsetTop,
- level: i,
- anchor: header.id,
- });
- }
- }
- }
- catalogItems.value.sort((a, b) => {
- return a.scrollPos - b.scrollPos;
- })
- }
- watch(() => props.contents, () => {
- setTimeout(() => {
- generateCatalog();
- }, 200);
- }, { immediate: true })
- onBeforeUnmount(() => {
- if (lastStyleTag) {
- lastStyleTag.parentElement?.removeChild(lastStyleTag);
- lastStyleTag = null;
- }
- })
- onMounted(() => {
- genTagCss()
- });
- </script>
- <style lang="scss">
- .nana-rich-html-container {
- position: relative;
- display: flex;
- flex-direction: row;
- .rich-html {
- flex: 1 1 100%;
- }
- .rich-html-catalog {
- position: fixed;
- top: 140px;
- right: 0;
- bottom: 0px;
- width: 15rem;
- }
- }
- @media (max-width: 1648px) {
- .rich-html-catalog {
- display: none;
- }
- }
- </style>
|