docs: ✏️ 文档侧边栏新增版本号显示

This commit is contained in:
不如摸鱼去 2025-07-08 17:50:28 +08:00
parent 372735a16a
commit 6c6d5c9ea3
39 changed files with 473 additions and 36 deletions

View File

@ -1,7 +1,7 @@
/*
* @Author: weisheng
* @Date: 2023-07-27 10:26:09
* @LastEditTime: 2025-07-07 20:46:21
* @LastEditTime: 2025-07-08 17:45:58
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/docs/.vitepress/config.mts
@ -11,6 +11,7 @@ import { defineConfig } from 'vitepress';
import viteCompression from 'vite-plugin-compression'
import { fileURLToPath, URL } from 'node:url'
import { MarkdownTransform } from './plugins/markdown-transform'
import { VersionBadgePlugin } from './plugins/version-badge'
import llmstxt from 'vitepress-plugin-llms'
import enUS from './locales/en-US'
import zhCN from './locales/zh-CN'
@ -22,6 +23,7 @@ export default defineConfig({
domain: 'https://wot-design-uni.cn',
}),
MarkdownTransform(),
VersionBadgePlugin(),
viteCompression({
verbose: true,
disable: false,
@ -62,6 +64,12 @@ export default defineConfig({
replacement: fileURLToPath(
new URL('./theme/components/VPNavBar.vue', import.meta.url)
)
},
{
find: /^.*\/VPSidebarItem\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/VPSidebarItem.vue', import.meta.url)
)
}
]
}

View File

@ -0,0 +1,10 @@
// 自动生成的版本数据
// 此文件由插件自动生成,请勿手动修改
// 如需修改版本信息,请在对应的 Markdown 文件 frontmatter 中修改
export const versionData = {
"/component/count-to": "1.3.8",
"/component/keyboard": "1.3.10",
"/component/root-portal": "$LOWEST_VERSION$",
"/component/signature": "1.6.0"
}

View File

@ -0,0 +1,87 @@
import type { Plugin } from 'vite'
import { readFileSync, existsSync, writeFileSync } from 'fs'
import { resolve } from 'path'
import fs from 'fs'
// 从 Markdown 文件中提取版本信息
function extractVersionFromMarkdown(filePath: string): string | null {
if (!existsSync(filePath)) return null
try {
const content = readFileSync(filePath, 'utf-8')
const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/)
if (frontmatterMatch) {
const frontmatter = frontmatterMatch[1]
const versionMatch = frontmatter.match(/version:\s*(['"]?)([^'"\n]+)\1/)
if (versionMatch) {
return versionMatch[2].trim()
}
}
} catch (e) {
console.warn(`Failed to read file ${filePath}:`, e)
}
return null
}
// 扫描组件文档目录,提取版本信息
function scanComponentVersions(): Record<string, string> {
const versionData: Record<string, string> = {}
const componentDir = resolve(__dirname, '../../component')
if (!existsSync(componentDir)) {
console.warn('Component directory not found:', componentDir)
return versionData
}
try {
const files = fs.readdirSync(componentDir).filter((file: string) => file.endsWith('.md'))
files.forEach((filename: string) => {
const filePath = resolve(componentDir, filename)
const version = extractVersionFromMarkdown(filePath)
if (version) {
const path = `/component/${filename.replace('.md', '')}`
versionData[path] = version
console.log(`✅ Found version info in ${filename}:`, version)
}
})
} catch (e) {
console.warn('Failed to scan component directory:', e)
}
return versionData
}
// 生成版本数据文件
function generateVersionData(): string {
const versionData = scanComponentVersions()
return `// 自动生成的版本数据
// 此文件由插件自动生成,请勿手动修改
// 如需修改版本信息,请在对应的 Markdown 文件 frontmatter 中修改
export const versionData = ${JSON.stringify(versionData, null, 2)}
`
}
export function VersionBadgePlugin(): Plugin {
return {
name: 'version-badge',
configResolved() {
// 在配置解析后生成版本数据
try {
const versionDataContent = generateVersionData()
const outputPath = resolve(__dirname, '../config/version-data.ts')
writeFileSync(outputPath, versionDataContent, 'utf-8')
console.log('✅ Version data generated successfully')
} catch (e) {
console.error('❌ Failed to generate version data:', e)
}
}
}
}

View File

@ -0,0 +1,311 @@
<script setup lang="ts">
import type { DefaultTheme } from 'vitepress/theme'
import { computed } from 'vue'
import { useSidebarControl } from 'vitepress/dist/client/theme-default/composables/sidebar.js'
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
import { versionData } from '../../config/version-data'
const props = defineProps<{
item: DefaultTheme.SidebarItem
depth: number
}>()
const {
collapsed,
collapsible,
isLink,
isActiveLink,
hasActiveLink,
hasChildren,
toggle
} = useSidebarControl(computed(() => props.item))
const sectionTag = computed(() => (hasChildren.value ? 'section' : `div`))
const linkTag = computed(() => (isLink.value ? 'a' : 'div'))
const textTag = computed(() => {
return !hasChildren.value
? 'p'
: props.depth + 2 === 7
? 'p'
: `h${props.depth + 2}`
})
const itemRole = computed(() => (isLink.value ? undefined : 'button'))
const classes = computed(() => [
[`level-${props.depth}`],
{ collapsible: collapsible.value },
{ collapsed: collapsed.value },
{ 'is-link': isLink.value },
{ 'is-active': isActiveLink.value },
{ 'has-active': hasActiveLink.value }
])
//
const versionInfo = computed(() => {
if (!props.item.link) return null
//
const path = props.item.link
const version = versionData[path]
if (!version) return null
return {
text: version,
type: 'warning' // 使warning
}
})
function onItemInteraction(e: MouseEvent | Event) {
if ('key' in e && e.key !== 'Enter') {
return
}
!props.item.link && toggle()
}
function onCaretClick() {
props.item.link && toggle()
}
</script>
<template>
<component :is="sectionTag" class="VPSidebarItem" :class="classes">
<div
v-if="item.text"
class="item"
:role="itemRole"
v-on="
item.items
? { click: onItemInteraction, keydown: onItemInteraction }
: {}
"
:tabindex="item.items && 0"
>
<div class="indicator" />
<VPLink
v-if="item.link"
:tag="linkTag"
class="link"
:href="item.link"
:rel="item.rel"
:target="item.target"
>
<component :is="textTag" class="text">
{{ item.text }}
<el-tag
v-if="versionInfo"
size="small"
effect="plain"
round
class="version-tag"
>
{{ versionInfo.text }}
</el-tag>
</component>
</VPLink>
<component v-else :is="textTag" class="text">
{{ item.text }}
<el-tag
v-if="versionInfo"
size="small"
effect="plain"
round
class="version-tag"
>
{{ versionInfo.text }}
</el-tag>
</component>
<div
v-if="item.collapsed != null && item.items && item.items.length"
class="caret"
role="button"
aria-label="toggle section"
@click="onCaretClick"
@keydown.enter="onCaretClick"
tabindex="0"
>
<span class="vpi-chevron-right caret-icon" />
</div>
</div>
<div v-if="item.items && item.items.length" class="items">
<template v-if="depth < 5">
<VPSidebarItem
v-for="i in item.items"
:key="i.text"
:item="i"
:depth="depth + 1"
/>
</template>
</div>
</component>
</template>
<style scoped>
.VPSidebarItem.level-0 {
padding-bottom: 24px;
}
.VPSidebarItem.collapsed.level-0 {
padding-bottom: 10px;
}
.item {
position: relative;
display: flex;
width: 100%;
}
.VPSidebarItem.collapsible > .item {
cursor: pointer;
}
.indicator {
position: absolute;
top: 6px;
bottom: 6px;
left: -17px;
width: 2px;
border-radius: 2px;
transition: background-color 0.25s;
}
.VPSidebarItem.level-2.is-active > .item > .indicator,
.VPSidebarItem.level-3.is-active > .item > .indicator,
.VPSidebarItem.level-4.is-active > .item > .indicator,
.VPSidebarItem.level-5.is-active > .item > .indicator {
background-color: var(--vp-c-brand-1);
}
.link {
display: flex;
align-items: center;
flex-grow: 1;
}
.text {
flex-grow: 1;
padding: 4px 0;
line-height: 24px;
font-size: 14px;
transition: color 0.25s;
display: flex;
align-items: center;
gap: 8px;
}
.VPSidebarItem.level-0 .text {
font-weight: 700;
color: var(--vp-c-text-1);
}
.VPSidebarItem.level-1 .text,
.VPSidebarItem.level-2 .text,
.VPSidebarItem.level-3 .text,
.VPSidebarItem.level-4 .text,
.VPSidebarItem.level-5 .text {
font-weight: 500;
color: var(--vp-c-text-2);
}
.VPSidebarItem.level-0.is-link > .item > .link:hover .text,
.VPSidebarItem.level-1.is-link > .item > .link:hover .text,
.VPSidebarItem.level-2.is-link > .item > .link:hover .text,
.VPSidebarItem.level-3.is-link > .item > .link:hover .text,
.VPSidebarItem.level-4.is-link > .item > .link:hover .text,
.VPSidebarItem.level-5.is-link > .item > .link:hover .text {
color: var(--vp-c-brand-1);
}
.VPSidebarItem.level-0.has-active > .item > .text,
.VPSidebarItem.level-1.has-active > .item > .text,
.VPSidebarItem.level-2.has-active > .item > .text,
.VPSidebarItem.level-3.has-active > .item > .text,
.VPSidebarItem.level-4.has-active > .item > .text,
.VPSidebarItem.level-5.has-active > .item > .text,
.VPSidebarItem.level-0.has-active > .item > .link > .text,
.VPSidebarItem.level-1.has-active > .item > .link > .text,
.VPSidebarItem.level-2.has-active > .item > .link > .text,
.VPSidebarItem.level-3.has-active > .item > .link > .text,
.VPSidebarItem.level-4.has-active > .item > .link > .text,
.VPSidebarItem.level-5.has-active > .item > .link > .text {
color: var(--vp-c-text-1);
}
.VPSidebarItem.level-0.is-active > .item .link > .text,
.VPSidebarItem.level-1.is-active > .item .link > .text,
.VPSidebarItem.level-2.is-active > .item .link > .text,
.VPSidebarItem.level-3.is-active > .item .link > .text,
.VPSidebarItem.level-4.is-active > .item .link > .text,
.VPSidebarItem.level-5.is-active > .item .link > .text {
color: var(--vp-c-brand-1);
}
.caret {
display: flex;
justify-content: center;
align-items: center;
margin-right: -7px;
width: 32px;
height: 32px;
color: var(--vp-c-text-3);
cursor: pointer;
transition: color 0.25s;
flex-shrink: 0;
}
.item:hover .caret {
color: var(--vp-c-text-2);
}
.item:hover .caret:hover {
color: var(--vp-c-text-1);
}
.caret-icon {
font-size: 18px;
/*rtl:ignore*/
transform: rotate(90deg);
transition: transform 0.25s;
}
.VPSidebarItem.collapsed .caret-icon {
transform: rotate(0)/*rtl:rotate(180deg)*/;
}
.VPSidebarItem.level-1 .items,
.VPSidebarItem.level-2 .items,
.VPSidebarItem.level-3 .items,
.VPSidebarItem.level-4 .items,
.VPSidebarItem.level-5 .items {
border-left: 1px solid var(--vp-c-divider);
padding-left: 16px;
}
.VPSidebarItem.collapsed .items {
display: none;
}
/* 版本标签样式 */
.version-tag {
margin-left: 4px;
flex-shrink: 0;
}
/* 深色模式适配 */
:root.dark .version-tag {
opacity: 0.9;
}
/* 响应式设计 */
@media (max-width: 960px) {
.version-tag {
font-size: 10px;
}
}
</style>

1
docs/.vitepress/types/sidebar.d.ts vendored Normal file
View File

@ -0,0 +1 @@

View File

@ -1,4 +1,4 @@
# Circle 环形进度条 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.19</el-tag>
# Circle 环形进度条
圆环形的进度条组件,支持进度渐变动画。

View File

@ -1,4 +1,4 @@
# CountDown 倒计时<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.58</el-tag>
# CountDown 倒计时
用于实时展示倒计时数值,支持毫秒精度。

View File

@ -1,4 +1,7 @@
# CountTo 数字滚动<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.8</el-tag>
---
version: 1.3.8
---
# CountTo 数字滚动
数字滚动组件。

View File

@ -1,4 +1,4 @@
# Icon 图标 <el-tag text style="vertical-align: middle;margin-left:8px;" type="warning">0.1.27 更新</el-tag>
# Icon 图标
基于字体的图标集。

View File

@ -1,4 +1,4 @@
# IndexBar 索引栏 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.2.21</el-tag>
# IndexBar 索引栏
用于列表的索引分类显示和快速定位。

View File

@ -1,4 +1,7 @@
# Keyboard 虚拟键盘 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.10</el-tag>
---
version: 1.3.10
---
# Keyboard 虚拟键盘
虚拟数字键盘,用于输入数字、密码、身份证或车牌号等场景。

View File

@ -1,4 +1,4 @@
# Overlay 遮罩层 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.30</el-tag>
# Overlay 遮罩层
创建一个遮罩层,用于强调特定的页面元素,并阻止用户进行其他操作。

View File

@ -1,4 +1,4 @@
# PasswordInput 密码输入框 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
# PasswordInput 密码输入框
带网格的输入框组件,可以用于输入密码、短信验证码等场景,通常与[数字键盘](./number-keyboard.md)组件配合使用。

View File

@ -1,4 +1,7 @@
# Root Portal 根节点传送<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">$LOWEST_VERSION$</el-tag>
---
version: $LOWEST_VERSION$
---
# Root Portal 根节点传送
是否从页面中脱离出来,用于解决各种 fixed 失效问题,主要用于制作弹窗、弹出层等。

View File

@ -1,4 +1,4 @@
# Segmented 分段器 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.23</el-tag>
# Segmented 分段器
分段器用于展示多个选项并允许用户选择其中单个选项。

View File

@ -1,4 +1,4 @@
# SelectPicker 单复选选择器 <el-tag text style="vertical-align: middle;margin-left:8px;" type="warning">0.1.34 更新</el-tag>
# SelectPicker 单复选选择器
用于从一组选项中进行单选或多选。

View File

@ -1,4 +1,4 @@
# Sidebar 侧边导航 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.49</el-tag>
# Sidebar 侧边导航
垂直展示的导航栏,用于在不同的内容区域之间进行切换。

View File

@ -1,4 +1,7 @@
# Signature 签名 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.6.0</el-tag>
---
version: 1.6.0
---
# Signature 签名
用于签名场景,基于 Canvas 实现的签名组件。提供了基础签名、历史记录、笔锋效果等功能。

View File

@ -1,4 +1,4 @@
# Table 表格 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.39</el-tag>
# Table 表格
用于展示多条结构类似的数据, 可对数据进行排序等操作。

View File

@ -1,4 +1,4 @@
# Textarea 文本域 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
# Textarea 文本域
用于输入多行文本信息。

View File

@ -1,5 +1,4 @@
# Watermark 水印 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.16</el-tag>
# Watermark 水印
在页面或组件上添加指定的图片或文字,可用于版权保护、品牌宣传等场景。

View File

@ -1,4 +1,4 @@
# Circle Progress Bar <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.19</el-tag>
# Circle Progress Bar
A circular progress bar component that supports progress gradient animation.

View File

@ -1,4 +1,4 @@
# CountDown<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.58</el-tag>
# CountDown
Used to display countdown values in real-time, supporting millisecond precision.

View File

@ -1,4 +1,4 @@
# CountTo Number Animation<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.8</el-tag>
# CountTo Number Animation
Number animation component.

View File

@ -1,4 +1,4 @@
# IndexBar <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.2.21</el-tag>
# IndexBar
Used for displaying index classification and quick positioning of lists.

View File

@ -1,4 +1,7 @@
# Keyboard <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.10</el-tag>
---
version: 1.3.10
---
# Keyboard
Virtual keyboard for inputting numbers, passwords, ID cards, or license plate numbers.

View File

@ -1,4 +1,4 @@
# MessageBox <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.10</el-tag>
# MessageBox
A dialog box that pops up, commonly used for message prompts, message confirmation, etc., supports function calls.

View File

@ -1,4 +1,4 @@
# Overlay <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.30</el-tag>
# Overlay
Create a mask layer to emphasize specific page elements and prevent users from performing other operations.

View File

@ -1,4 +1,4 @@
# PasswordInput <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
# PasswordInput
A grid input box component that can be used for inputting passwords, SMS verification codes, and other scenarios. Usually used in conjunction with the [number keyboard](./number-keyboard.md) component.

View File

@ -1,4 +1,7 @@
# Root Portal<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">$LOWEST_VERSION$</el-tag>
---
version: $LOWEST_VERSION$
---
# Root Portal
Whether to break out of the page, used to solve various fixed positioning issues, mainly used for making popups and overlays.

View File

@ -1,4 +1,4 @@
# Segmented <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.23</el-tag>
# Segmented
Segmented is used to display multiple options and allow users to select a single option.

View File

@ -1,4 +1,4 @@
# SelectPicker <el-tag text style="vertical-align: middle;margin-left:8px;" type="warning">0.1.34 Update</el-tag>
# SelectPicker
Used for single or multiple selection from a set of options.

View File

@ -1,4 +1,4 @@
# Sidebar <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.49</el-tag>
# Sidebar
A vertical navigation bar used to switch between different content areas.

View File

@ -1,4 +1,7 @@
# Signature <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.6.0</el-tag>
---
version: 1.6.0
---
# Signature
A signature component based on Canvas for signature scenarios. Provides basic signature, history record, and pressure effect features.

View File

@ -1,4 +1,4 @@
# Table <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.39</el-tag>
# Table
Used to display multiple pieces of data with similar structures, allowing operations such as sorting.

View File

@ -1,4 +1,4 @@
'# Textarea <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
# Textarea
Used for inputting multi-line text information.

View File

@ -1,4 +1,4 @@
# Watermark <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.16</el-tag>
# Watermark
Add specified images or text on pages or components, which can be used for copyright protection, brand promotion, and other scenarios.

View File

@ -1,4 +1,4 @@
# Internationalization<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.20</el-tag>
# Internationalization
Wot UI uses Chinese by default and supports multi-language switching. If you want to use other languages, you can refer to the solutions below.

View File

@ -1,4 +1,4 @@
# 国际化<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.20</el-tag>
# 国际化
Wot UI 默认使用中文语言,同时支持多语言切换,如果你希望使用其他语言,你可以参考下面的方案。