md2ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -0
- package/bin/md2ui.js +168 -0
- package/index.html +14 -0
- package/package.json +46 -0
- package/public/README.md +106 -0
- package/public/docs/00-/345/277/253/351/200/237/345/274/200/345/247/213.md +51 -0
- package/public/docs/01-/345/212/237/350/203/275/347/211/271/346/200/247.md +57 -0
- package/public/docs/02-Mermaid/345/233/276/350/241/250.md +102 -0
- package/public/docs/03-/350/277/233/351/230/266/346/214/207/345/215/227/01-/347/233/256/345/275/225/347/273/223/346/236/204.md +55 -0
- package/public/docs/03-/350/277/233/351/230/266/346/214/207/345/215/227/02-/350/207/252/345/256/232/344/271/211/351/205/215/347/275/256.md +63 -0
- package/public/docs/03-/350/277/233/351/230/266/346/214/207/345/215/227/03-/351/203/250/347/275/262/346/226/271/346/241/210.md +73 -0
- package/public/docs/04-API/345/217/202/350/200/203/01-/347/273/204/344/273/266API.md +80 -0
- package/public/docs/04-API/345/217/202/350/200/203/02-Composables.md +92 -0
- package/public/logo.svg +6 -0
- package/src/App.vue +171 -0
- package/src/api/docs.js +103 -0
- package/src/components/ImageZoom.vue +285 -0
- package/src/components/Logo.vue +44 -0
- package/src/components/TableOfContents.vue +44 -0
- package/src/components/TreeNode.vue +61 -0
- package/src/composables/useMarkdown.js +128 -0
- package/src/composables/useResize.js +50 -0
- package/src/composables/useScroll.js +77 -0
- package/src/main.js +5 -0
- package/src/style.css +784 -0
- package/vite.config.js +9 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Mermaid 图表
|
|
2
|
+
|
|
3
|
+
md2ui 内置了 Mermaid 支持,可以直接在 Markdown 中绘制各类图表。
|
|
4
|
+
|
|
5
|
+
## 流程图
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart TD
|
|
9
|
+
A[开始] --> B{是否有文档?}
|
|
10
|
+
B -->|是| C[解析 Markdown]
|
|
11
|
+
B -->|否| D[显示空状态]
|
|
12
|
+
C --> E[渲染 HTML]
|
|
13
|
+
E --> F[展示页面]
|
|
14
|
+
D --> F
|
|
15
|
+
F --> G[结束]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 时序图
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
sequenceDiagram
|
|
22
|
+
participant U as 用户
|
|
23
|
+
participant B as 浏览器
|
|
24
|
+
participant S as 服务器
|
|
25
|
+
|
|
26
|
+
U->>B: 点击文档链接
|
|
27
|
+
B->>S: 请求 Markdown 文件
|
|
28
|
+
S-->>B: 返回文件内容
|
|
29
|
+
B->>B: 解析并渲染
|
|
30
|
+
B-->>U: 显示文档页面
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 类图
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
classDiagram
|
|
37
|
+
class Document {
|
|
38
|
+
+String title
|
|
39
|
+
+String content
|
|
40
|
+
+Date createdAt
|
|
41
|
+
+render()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class Folder {
|
|
45
|
+
+String name
|
|
46
|
+
+Document[] documents
|
|
47
|
+
+Folder[] subfolders
|
|
48
|
+
+expand()
|
|
49
|
+
+collapse()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class Navigator {
|
|
53
|
+
+Folder root
|
|
54
|
+
+Document current
|
|
55
|
+
+navigate(path)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Folder "1" *-- "*" Document
|
|
59
|
+
Folder "1" *-- "*" Folder
|
|
60
|
+
Navigator --> Folder
|
|
61
|
+
Navigator --> Document
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 状态图
|
|
65
|
+
|
|
66
|
+
```mermaid
|
|
67
|
+
stateDiagram-v2
|
|
68
|
+
[*] --> 空闲
|
|
69
|
+
空闲 --> 加载中: 请求文档
|
|
70
|
+
加载中 --> 已加载: 加载成功
|
|
71
|
+
加载中 --> 错误: 加载失败
|
|
72
|
+
已加载 --> 空闲: 切换文档
|
|
73
|
+
错误 --> 空闲: 重试
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 甘特图
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
gantt
|
|
80
|
+
title 项目开发计划
|
|
81
|
+
dateFormat YYYY-MM-DD
|
|
82
|
+
section 基础功能
|
|
83
|
+
Markdown 渲染 :done, a1, 2024-01-01, 7d
|
|
84
|
+
目录导航 :done, a2, after a1, 5d
|
|
85
|
+
section 增强功能
|
|
86
|
+
Mermaid 支持 :done, b1, after a2, 3d
|
|
87
|
+
代码高亮 :done, b2, after b1, 2d
|
|
88
|
+
section 优化
|
|
89
|
+
性能优化 :active, c1, after b2, 5d
|
|
90
|
+
响应式适配 :c2, after c1, 3d
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 饼图
|
|
94
|
+
|
|
95
|
+
```mermaid
|
|
96
|
+
pie title 技术栈占比
|
|
97
|
+
"Vue 3" : 40
|
|
98
|
+
"Vite" : 20
|
|
99
|
+
"Marked" : 15
|
|
100
|
+
"Mermaid" : 15
|
|
101
|
+
"其他" : 10
|
|
102
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 目录结构
|
|
2
|
+
|
|
3
|
+
了解 md2ui 的项目结构,有助于你进行定制开发。
|
|
4
|
+
|
|
5
|
+
## 项目结构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
md2ui/
|
|
9
|
+
├── public/ # 静态资源
|
|
10
|
+
│ ├── README.md # 首页内容
|
|
11
|
+
│ ├── logo.svg # 网站图标
|
|
12
|
+
│ └── docs/ # 文档目录
|
|
13
|
+
├── src/
|
|
14
|
+
│ ├── api/ # API 接口
|
|
15
|
+
│ │ └── docs.js # 文档列表获取
|
|
16
|
+
│ ├── components/ # Vue 组件
|
|
17
|
+
│ │ ├── Logo.vue # Logo 组件
|
|
18
|
+
│ │ ├── TreeNode.vue # 树形节点
|
|
19
|
+
│ │ ├── TableOfContents.vue # 目录组件
|
|
20
|
+
│ │ └── ImageZoom.vue # 图片放大
|
|
21
|
+
│ ├── composables/ # 组合式函数
|
|
22
|
+
│ │ ├── useMarkdown.js # Markdown 渲染
|
|
23
|
+
│ │ ├── useScroll.js # 滚动处理
|
|
24
|
+
│ │ └── useResize.js # 拖拽调整
|
|
25
|
+
│ ├── App.vue # 主组件
|
|
26
|
+
│ ├── main.js # 入口文件
|
|
27
|
+
│ └── style.css # 全局样式
|
|
28
|
+
├── index.html # HTML 模板
|
|
29
|
+
├── vite.config.js # Vite 配置
|
|
30
|
+
└── package.json # 项目配置
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 核心模块说明
|
|
34
|
+
|
|
35
|
+
### api/docs.js
|
|
36
|
+
|
|
37
|
+
负责扫描 `public/docs/` 目录,构建文档树结构。
|
|
38
|
+
|
|
39
|
+
### composables/useMarkdown.js
|
|
40
|
+
|
|
41
|
+
封装 Markdown 解析逻辑,包括:
|
|
42
|
+
- 使用 marked 解析 Markdown
|
|
43
|
+
- 提取标题生成目录
|
|
44
|
+
- 处理 Mermaid 代码块
|
|
45
|
+
|
|
46
|
+
### composables/useScroll.js
|
|
47
|
+
|
|
48
|
+
处理滚动相关逻辑:
|
|
49
|
+
- 计算阅读进度
|
|
50
|
+
- 高亮当前章节
|
|
51
|
+
- 返回顶部功能
|
|
52
|
+
|
|
53
|
+
### composables/useResize.js
|
|
54
|
+
|
|
55
|
+
实现拖拽调整宽度功能。
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 自定义配置
|
|
2
|
+
|
|
3
|
+
md2ui 支持多种自定义配置,满足不同场景需求。
|
|
4
|
+
|
|
5
|
+
## 修改网站标题
|
|
6
|
+
|
|
7
|
+
编辑 `index.html`:
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<title>你的文档站点</title>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 修改 Logo
|
|
14
|
+
|
|
15
|
+
编辑 `src/components/Logo.vue`:
|
|
16
|
+
|
|
17
|
+
```vue
|
|
18
|
+
<span class="logo-text">你的项目名</span>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
或替换 `public/logo.svg` 图标文件。
|
|
22
|
+
|
|
23
|
+
## 修改首页
|
|
24
|
+
|
|
25
|
+
编辑 `public/README.md` 文件,这是点击 Logo 后显示的首页内容。
|
|
26
|
+
|
|
27
|
+
## 修改端口
|
|
28
|
+
|
|
29
|
+
编辑 `vite.config.js`:
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
server: {
|
|
34
|
+
port: 3000
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 样式定制
|
|
40
|
+
|
|
41
|
+
全局样式在 `src/style.css` 中,主要变量:
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
/* 颜色 */
|
|
45
|
+
--primary-color: #3b82f6;
|
|
46
|
+
--text-color: #1f2937;
|
|
47
|
+
--bg-color: #ffffff;
|
|
48
|
+
--border-color: #e5e7eb;
|
|
49
|
+
|
|
50
|
+
/* 尺寸 */
|
|
51
|
+
--sidebar-width: 280px;
|
|
52
|
+
--toc-width: 240px;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 文档排序规则
|
|
56
|
+
|
|
57
|
+
文件名格式:`序号-名称.md`
|
|
58
|
+
|
|
59
|
+
- `00-快速开始.md` 排在最前
|
|
60
|
+
- `01-使用指南.md` 排在其后
|
|
61
|
+
- 没有序号的文件按字母排序
|
|
62
|
+
|
|
63
|
+
文件夹同样支持序号前缀。
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# 部署方案
|
|
2
|
+
|
|
3
|
+
md2ui 构建后是纯静态文件,可部署到任意静态服务器。
|
|
4
|
+
|
|
5
|
+
## 构建
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm build
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
构建产物在 `dist/` 目录。
|
|
12
|
+
|
|
13
|
+
## Nginx 部署
|
|
14
|
+
|
|
15
|
+
```nginx
|
|
16
|
+
server {
|
|
17
|
+
listen 80;
|
|
18
|
+
server_name docs.example.com;
|
|
19
|
+
root /var/www/md2ui/dist;
|
|
20
|
+
index index.html;
|
|
21
|
+
|
|
22
|
+
# 支持 HTML5 History 模式
|
|
23
|
+
location / {
|
|
24
|
+
try_files $uri $uri/ /index.html;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# 静态资源缓存
|
|
28
|
+
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
|
29
|
+
expires 30d;
|
|
30
|
+
add_header Cache-Control "public, immutable";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Docker 部署
|
|
36
|
+
|
|
37
|
+
项目已包含 Dockerfile:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# 构建镜像
|
|
41
|
+
docker build -t md2ui .
|
|
42
|
+
|
|
43
|
+
# 运行容器
|
|
44
|
+
docker run -d -p 3000:3000 md2ui
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 部署流程
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
flowchart LR
|
|
51
|
+
A[代码提交] --> B[CI 构建]
|
|
52
|
+
B --> C[生成 dist]
|
|
53
|
+
C --> D{部署目标}
|
|
54
|
+
D --> E[Nginx]
|
|
55
|
+
D --> F[Docker]
|
|
56
|
+
D --> G[CDN]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## GitHub Pages
|
|
60
|
+
|
|
61
|
+
1. 修改 `vite.config.js` 设置 base 路径:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
export default defineConfig({
|
|
65
|
+
base: '/your-repo-name/'
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
2. 构建并推送到 gh-pages 分支
|
|
70
|
+
|
|
71
|
+
## Vercel / Netlify
|
|
72
|
+
|
|
73
|
+
直接连接 Git 仓库,自动构建部署,无需额外配置。
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# 组件 API
|
|
2
|
+
|
|
3
|
+
md2ui 的核心组件及其接口说明。
|
|
4
|
+
|
|
5
|
+
## TreeNode
|
|
6
|
+
|
|
7
|
+
树形节点组件,用于渲染文档导航。
|
|
8
|
+
|
|
9
|
+
### Props
|
|
10
|
+
|
|
11
|
+
| 属性 | 类型 | 说明 |
|
|
12
|
+
|------|------|------|
|
|
13
|
+
| item | Object | 节点数据 |
|
|
14
|
+
| currentDoc | String | 当前选中的文档 |
|
|
15
|
+
|
|
16
|
+
### Events
|
|
17
|
+
|
|
18
|
+
| 事件 | 参数 | 说明 |
|
|
19
|
+
|------|------|------|
|
|
20
|
+
| toggle | folder | 切换文件夹展开状态 |
|
|
21
|
+
| select | docKey | 选中文档 |
|
|
22
|
+
|
|
23
|
+
### item 数据结构
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
{
|
|
27
|
+
key: 'docs/guide.md', // 唯一标识
|
|
28
|
+
name: '使用指南', // 显示名称
|
|
29
|
+
type: 'file', // 类型: file | folder
|
|
30
|
+
path: '/docs/guide.md', // 文件路径
|
|
31
|
+
expanded: false, // 是否展开(仅文件夹)
|
|
32
|
+
children: [] // 子节点(仅文件夹)
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## TableOfContents
|
|
37
|
+
|
|
38
|
+
文档目录组件,显示当前文档的标题大纲。
|
|
39
|
+
|
|
40
|
+
### Props
|
|
41
|
+
|
|
42
|
+
| 属性 | 类型 | 说明 |
|
|
43
|
+
|------|------|------|
|
|
44
|
+
| tocItems | Array | 目录项列表 |
|
|
45
|
+
| activeHeading | String | 当前高亮的标题 ID |
|
|
46
|
+
| collapsed | Boolean | 是否折叠 |
|
|
47
|
+
| width | Number | 组件宽度 |
|
|
48
|
+
|
|
49
|
+
### Events
|
|
50
|
+
|
|
51
|
+
| 事件 | 参数 | 说明 |
|
|
52
|
+
|------|------|------|
|
|
53
|
+
| scroll-to | headingId | 跳转到指定标题 |
|
|
54
|
+
|
|
55
|
+
### tocItems 数据结构
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
[
|
|
59
|
+
{ id: 'heading-1', text: '快速开始', level: 1 },
|
|
60
|
+
{ id: 'heading-2', text: '安装', level: 2 },
|
|
61
|
+
{ id: 'heading-3', text: '配置', level: 2 }
|
|
62
|
+
]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## ImageZoom
|
|
66
|
+
|
|
67
|
+
图片放大组件。
|
|
68
|
+
|
|
69
|
+
### Props
|
|
70
|
+
|
|
71
|
+
| 属性 | 类型 | 说明 |
|
|
72
|
+
|------|------|------|
|
|
73
|
+
| visible | Boolean | 是否显示 |
|
|
74
|
+
| imageContent | String | 图片 HTML 内容 |
|
|
75
|
+
|
|
76
|
+
### Events
|
|
77
|
+
|
|
78
|
+
| 事件 | 说明 |
|
|
79
|
+
|------|------|
|
|
80
|
+
| close | 关闭放大视图 |
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Composables
|
|
2
|
+
|
|
3
|
+
md2ui 使用 Vue 3 Composition API,将可复用逻辑封装为 composables。
|
|
4
|
+
|
|
5
|
+
## useMarkdown
|
|
6
|
+
|
|
7
|
+
Markdown 渲染相关逻辑。
|
|
8
|
+
|
|
9
|
+
### 返回值
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
const {
|
|
13
|
+
htmlContent, // ref<string> - 渲染后的 HTML
|
|
14
|
+
tocItems, // ref<array> - 目录项列表
|
|
15
|
+
renderMarkdown // function - 渲染方法
|
|
16
|
+
} = useMarkdown()
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### renderMarkdown
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
// 渲染 Markdown 文本
|
|
23
|
+
await renderMarkdown(markdownText)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
处理流程:
|
|
27
|
+
|
|
28
|
+
```mermaid
|
|
29
|
+
flowchart TD
|
|
30
|
+
A[Markdown 文本] --> B[marked 解析]
|
|
31
|
+
B --> C[提取标题]
|
|
32
|
+
C --> D[处理 Mermaid]
|
|
33
|
+
D --> E[输出 HTML]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## useScroll
|
|
37
|
+
|
|
38
|
+
滚动相关逻辑。
|
|
39
|
+
|
|
40
|
+
### 返回值
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
const {
|
|
44
|
+
scrollProgress, // ref<number> - 阅读进度 0-100
|
|
45
|
+
showBackToTop, // ref<boolean> - 是否显示返回顶部
|
|
46
|
+
activeHeading, // ref<string> - 当前高亮标题 ID
|
|
47
|
+
handleScroll, // function - 滚动事件处理
|
|
48
|
+
scrollToHeading, // function - 跳转到标题
|
|
49
|
+
scrollToTop // function - 返回顶部
|
|
50
|
+
} = useScroll()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 使用示例
|
|
54
|
+
|
|
55
|
+
```vue
|
|
56
|
+
<template>
|
|
57
|
+
<main @scroll="handleScroll">
|
|
58
|
+
<!-- 内容 -->
|
|
59
|
+
</main>
|
|
60
|
+
<button v-if="showBackToTop" @click="scrollToTop">
|
|
61
|
+
返回顶部 {{ scrollProgress }}%
|
|
62
|
+
</button>
|
|
63
|
+
</template>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## useResize
|
|
67
|
+
|
|
68
|
+
拖拽调整宽度逻辑。
|
|
69
|
+
|
|
70
|
+
### 返回值
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
const {
|
|
74
|
+
sidebarWidth, // ref<number> - 侧边栏宽度
|
|
75
|
+
tocWidth, // ref<number> - 目录宽度
|
|
76
|
+
startResize // function - 开始拖拽
|
|
77
|
+
} = useResize()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 使用示例
|
|
81
|
+
|
|
82
|
+
```vue
|
|
83
|
+
<template>
|
|
84
|
+
<aside :style="{ width: sidebarWidth + 'px' }">
|
|
85
|
+
<!-- 侧边栏 -->
|
|
86
|
+
</aside>
|
|
87
|
+
<div
|
|
88
|
+
class="resizer"
|
|
89
|
+
@mousedown="startResize('left', $event)"
|
|
90
|
+
></div>
|
|
91
|
+
</template>
|
|
92
|
+
```
|
package/public/logo.svg
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect x="6" y="4" width="20" height="24" rx="2" stroke="#111827" stroke-width="2" fill="none"/>
|
|
3
|
+
<line x1="10" y1="10" x2="22" y2="10" stroke="#111827" stroke-width="2" stroke-linecap="round"/>
|
|
4
|
+
<line x1="10" y1="16" x2="22" y2="16" stroke="#111827" stroke-width="2" stroke-linecap="round"/>
|
|
5
|
+
<line x1="10" y1="22" x2="17" y2="22" stroke="#111827" stroke-width="2" stroke-linecap="round"/>
|
|
6
|
+
</svg>
|
package/src/App.vue
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="container">
|
|
3
|
+
<aside v-if="!sidebarCollapsed" class="sidebar" :style="{ width: sidebarWidth + 'px' }">
|
|
4
|
+
<div class="logo">
|
|
5
|
+
<Logo @go-home="loadReadme" />
|
|
6
|
+
</div>
|
|
7
|
+
<nav class="nav-menu">
|
|
8
|
+
<div class="nav-section">
|
|
9
|
+
<span>文档目录</span>
|
|
10
|
+
<div class="nav-actions">
|
|
11
|
+
<button class="action-btn" @click="expandAll" title="全部展开">
|
|
12
|
+
<ChevronsDownUp :size="14" />
|
|
13
|
+
</button>
|
|
14
|
+
<button class="action-btn" @click="collapseAll" title="全部收起">
|
|
15
|
+
<ChevronsUpDown :size="14" />
|
|
16
|
+
</button>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<TreeNode
|
|
20
|
+
v-for="item in docsList"
|
|
21
|
+
:key="item.key"
|
|
22
|
+
:item="item"
|
|
23
|
+
:currentDoc="currentDoc"
|
|
24
|
+
@toggle="toggleFolder"
|
|
25
|
+
@select="loadDoc"
|
|
26
|
+
/>
|
|
27
|
+
</nav>
|
|
28
|
+
</aside>
|
|
29
|
+
<div v-if="!sidebarCollapsed" class="resizer resizer-left" @mousedown="startResize('left', $event)"></div>
|
|
30
|
+
<button v-if="sidebarCollapsed" class="expand-btn expand-btn-left" @click="sidebarCollapsed = false" title="展开导航">
|
|
31
|
+
<PanelLeftOpen :size="16" />
|
|
32
|
+
</button>
|
|
33
|
+
<button v-else class="collapse-btn collapse-btn-left" @click="sidebarCollapsed = true" title="收起导航">
|
|
34
|
+
<PanelLeftClose :size="16" />
|
|
35
|
+
</button>
|
|
36
|
+
<main class="content" @scroll="handleScroll" @click="handleContentClick">
|
|
37
|
+
<article class="markdown-content" v-html="htmlContent"></article>
|
|
38
|
+
</main>
|
|
39
|
+
<div v-if="!tocCollapsed && tocItems.length > 0" class="resizer resizer-right" @mousedown="startResize('right', $event)"></div>
|
|
40
|
+
<TableOfContents :tocItems="tocItems" :activeHeading="activeHeading" :collapsed="tocCollapsed" :width="tocWidth" @scroll-to="scrollToHeading" />
|
|
41
|
+
<button v-if="tocCollapsed && tocItems.length > 0" class="expand-btn expand-btn-right" @click="tocCollapsed = false" title="展开目录">
|
|
42
|
+
<PanelRightOpen :size="16" />
|
|
43
|
+
</button>
|
|
44
|
+
<button v-else-if="tocItems.length > 0" class="collapse-btn collapse-btn-right" @click="tocCollapsed = true" title="收起目录">
|
|
45
|
+
<PanelRightClose :size="16" />
|
|
46
|
+
</button>
|
|
47
|
+
<transition name="fade">
|
|
48
|
+
<button v-if="showBackToTop" class="back-to-top" @click="scrollToTop" title="返回顶部">
|
|
49
|
+
<ArrowUp :size="20" />
|
|
50
|
+
<span class="progress-text">{{ scrollProgress }}%</span>
|
|
51
|
+
</button>
|
|
52
|
+
</transition>
|
|
53
|
+
<ImageZoom :visible="zoomVisible" :imageContent="zoomContent" @close="zoomVisible = false" />
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script setup>
|
|
58
|
+
import { ref, onMounted } from 'vue'
|
|
59
|
+
import { ChevronsDownUp, ChevronsUpDown, ArrowUp, PanelLeftOpen, PanelLeftClose, PanelRightOpen, PanelRightClose } from 'lucide-vue-next'
|
|
60
|
+
import Logo from './components/Logo.vue'
|
|
61
|
+
import TreeNode from './components/TreeNode.vue'
|
|
62
|
+
import TableOfContents from './components/TableOfContents.vue'
|
|
63
|
+
import ImageZoom from './components/ImageZoom.vue'
|
|
64
|
+
import { getDocsList } from './api/docs.js'
|
|
65
|
+
import { useMarkdown } from './composables/useMarkdown.js'
|
|
66
|
+
import { useScroll } from './composables/useScroll.js'
|
|
67
|
+
import { useResize } from './composables/useResize.js'
|
|
68
|
+
|
|
69
|
+
const docsList = ref([])
|
|
70
|
+
const currentDoc = ref('')
|
|
71
|
+
const sidebarCollapsed = ref(false)
|
|
72
|
+
const tocCollapsed = ref(false)
|
|
73
|
+
const zoomVisible = ref(false)
|
|
74
|
+
const zoomContent = ref('')
|
|
75
|
+
|
|
76
|
+
const { htmlContent, tocItems, renderMarkdown } = useMarkdown()
|
|
77
|
+
const { scrollProgress, showBackToTop, activeHeading, handleScroll, scrollToHeading, scrollToTop } = useScroll()
|
|
78
|
+
const { sidebarWidth, tocWidth, startResize } = useResize()
|
|
79
|
+
|
|
80
|
+
async function loadDocsList() {
|
|
81
|
+
docsList.value = await getDocsList()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function loadReadme() {
|
|
85
|
+
currentDoc.value = ''
|
|
86
|
+
try {
|
|
87
|
+
const response = await fetch('/README.md')
|
|
88
|
+
if (response.ok) {
|
|
89
|
+
const content = await response.text()
|
|
90
|
+
await renderMarkdown(content)
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('加载首页失败:', error)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function loadDoc(key) {
|
|
98
|
+
currentDoc.value = key
|
|
99
|
+
const doc = findDoc(docsList.value, key)
|
|
100
|
+
if (!doc) return
|
|
101
|
+
try {
|
|
102
|
+
const response = await fetch(doc.path)
|
|
103
|
+
if (response.ok) {
|
|
104
|
+
const content = await response.text()
|
|
105
|
+
await renderMarkdown(content)
|
|
106
|
+
const contentEl = document.querySelector('.content')
|
|
107
|
+
if (contentEl) contentEl.scrollTop = 0
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('加载文档失败:', error)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function findDoc(items, key) {
|
|
115
|
+
for (const item of items) {
|
|
116
|
+
if (item.type === 'file' && item.key === key) return item
|
|
117
|
+
if (item.type === 'folder' && item.children) {
|
|
118
|
+
const found = findDoc(item.children, key)
|
|
119
|
+
if (found) return found
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function toggleFolder(item) {
|
|
126
|
+
item.expanded = !item.expanded
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function expandAll() {
|
|
130
|
+
function expand(items) {
|
|
131
|
+
items.forEach(item => {
|
|
132
|
+
if (item.type === 'folder') {
|
|
133
|
+
item.expanded = true
|
|
134
|
+
if (item.children) expand(item.children)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
expand(docsList.value)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function collapseAll() {
|
|
142
|
+
function collapse(items) {
|
|
143
|
+
items.forEach(item => {
|
|
144
|
+
if (item.type === 'folder') {
|
|
145
|
+
item.expanded = false
|
|
146
|
+
if (item.children) collapse(item.children)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
collapse(docsList.value)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function handleContentClick(event) {
|
|
154
|
+
const target = event.target
|
|
155
|
+
if (target.tagName === 'IMG' && target.classList.contains('zoomable-image')) {
|
|
156
|
+
zoomContent.value = `<img src="${target.src}" alt="${target.alt || ''}" style="max-width: 100%; height: auto;" />`
|
|
157
|
+
zoomVisible.value = true
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
const mermaidEl = target.closest('.mermaid')
|
|
161
|
+
if (mermaidEl && mermaidEl.classList.contains('zoomable-image')) {
|
|
162
|
+
zoomContent.value = mermaidEl.innerHTML
|
|
163
|
+
zoomVisible.value = true
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
onMounted(async () => {
|
|
168
|
+
await loadDocsList()
|
|
169
|
+
await loadReadme()
|
|
170
|
+
})
|
|
171
|
+
</script>
|