91 lines
2.7 KiB
Vue
91 lines
2.7 KiB
Vue
<template>
|
||
<h3
|
||
:id
|
||
:class="['scroll-m-20 font-semibold tracking-tight [&:not(:first-child)]:mt-8 mb-3 flex items-center gap-2', themeSizeClass, themeDecorClass]"
|
||
:style="h3Style"
|
||
>
|
||
<span
|
||
v-if="showLeadingLine"
|
||
aria-hidden="true"
|
||
class="inline-block"
|
||
:class="themeLineClass"
|
||
:style="lineStyle"
|
||
/>
|
||
<NuxtLink
|
||
v-if="id && generate"
|
||
:to="`#${id}`"
|
||
>
|
||
<slot />
|
||
</NuxtLink>
|
||
<slot v-else />
|
||
</h3>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
const { id } = defineProps<{ id?: string }>()
|
||
|
||
const { headings } = useRuntimeConfig().public.mdc
|
||
const generate = computed(() => id && ((typeof headings?.anchorLinks === 'boolean' && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === 'object' && headings?.anchorLinks?.h3)))
|
||
|
||
// 主题风格:
|
||
// - classic:左侧竖线(主色),无额外背景
|
||
// - elegant:卡片式浅色主色背景 + 左侧竖线
|
||
// - minimal:底部虚线
|
||
const { selectedTheme, selectedThemeColor, customColor, themeColors } = useTheme()
|
||
|
||
const themeSizeClass = computed(() => {
|
||
return selectedTheme.value === 'classic'
|
||
? 'text-xl lg:text-1xl'
|
||
: 'text-1xl lg:text-2xl'
|
||
})
|
||
|
||
const isClassic = computed(() => selectedTheme.value === 'classic')
|
||
const isElegant = computed(() => selectedTheme.value === 'minimal')
|
||
const isMinimal = computed(() => selectedTheme.value === 'elegant')
|
||
|
||
const showLeadingLine = computed(() => isClassic.value || isMinimal.value || isElegant.value)
|
||
|
||
const primaryHex = computed(() => {
|
||
if (selectedThemeColor.value === 'custom') return customColor.value
|
||
const found = themeColors.find(c => c.value === selectedThemeColor.value)
|
||
return found?.color || '#3B82F6'
|
||
})
|
||
|
||
const themeDecorClass = computed(() => {
|
||
if (isElegant.value) return 'border-b border-dashed pb-1 border-primary dark:border-primary'
|
||
if (isMinimal.value) return 'px-3 py-2'
|
||
return ''
|
||
})
|
||
|
||
const themeLineClass = computed(() => {
|
||
return isClassic.value ? 'w-1 h-[0.9em]' : 'w-1 h-[1em]'
|
||
})
|
||
|
||
const lineStyle = computed(() => ({
|
||
backgroundColor: primaryHex.value,
|
||
borderRadius: '4px'
|
||
}))
|
||
|
||
function hexToRgba(hex: string, alpha: number): string {
|
||
const normalized = String(hex).replace('#', '')
|
||
const bigint = parseInt(normalized.length === 3
|
||
? normalized.split('').map(ch => ch + ch).join('')
|
||
: normalized, 16)
|
||
const r = (bigint >> 16) & 255
|
||
const g = (bigint >> 8) & 255
|
||
const b = bigint & 255
|
||
return `rgba(${r}, ${g}, ${b}, ${Math.min(Math.max(alpha, 0), 1)})`
|
||
}
|
||
|
||
const h3Style = computed(() => {
|
||
if (isMinimal.value) {
|
||
return {
|
||
backgroundColor: hexToRgba(primaryHex.value, 0.08),
|
||
border: `1px solid ${hexToRgba(primaryHex.value, 0.2)}`,
|
||
borderRadius: 'var(--ui-card-radius)'
|
||
}
|
||
}
|
||
return {}
|
||
})
|
||
</script>
|