This commit is contained in:
oneao committed 2026-10-09 17:32:14 +08:00
1 parent e99a9fb274
commit 0be0b0767a
788 files changed
+112023 -14941

No files matched your search

@@ -0,0 +1,67 @@
<script setup lang="ts">
/**
* TOC 条目(递归)—— 照抄 Docus / @nuxt/ui ContentToc 的 ul>li>a 结构。
*
* 实测(docus.dev,1440px / light):
* ul.list / ul.listWithChildren
* a.link display:flex; align-items:center; padding:4px 0;
* font-size:14px; line-height:20px; height:28px
* border-radius:4px(rounded-sm)
* color: text-muted
* a.link:active color: text-primary;font-weight 仍为 400
* h3 层级由父级 ul 的缩进体现(ms-3 = 12px)
*/
import { computed } from 'vue'
interface TocLink {
id: string
text: string
children?: TocLink[]
}
const props = defineProps<{
link: TocLink
level: number
activeIds: string[]
}>()
/** 点击时通知父级锁定该项为激活,避免滚动动画期间被 scrollspy 抢走 */
const emit = defineEmits<{ navigate: [id: string] }>()
const isActive = computed(() => props.activeIds.includes(props.link.id))
const linkClass = computed(() => ['g3-toc-link', isActive.value ? 'is-active' : ''])
function onClick(e: MouseEvent) {
const el = document.getElementById(props.link.id)
if (!el) return
e.preventDefault()
// 先锁定,再滚动:这样无论落点是多少,点击的项一定是高亮的
emit('navigate', props.link.id)
el.focus({ preventScroll: true })
// 滚到与 scrollspy 判据一致的位置:标题顶部对齐视口顶部下方一点,
// 使「绝对位置 <= scrollY + 余量」在滚动结束后必然成立。
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
history.replaceState(null, '', `#${props.link.id}`)
}
</script>
<template>
<li class="g3-toc-item">
<a :class="linkClass" :href="`#${link.id}`" @click="onClick">
<span class="g3-toc-link-text">{{ link.text }}</span>
</a>
<ul v-if="link.children?.length" class="g3-toc-list-with-children">
<G3TocItem
v-for="child in link.children"
:key="child.id"
:link="child"
:level="level + 1"
:active-ids="activeIds"
@navigate="emit('navigate', $event)"
/>
</ul>
</li>
</template>