vue-doc-readme
v1.0.6
Published
Vue3 报表说明组件,支持 Markdown 展示和 PDF 导出,支持零配置自动识别
Maintainers
Readme
vue-doc-readme
Vue3 文档说明组件,支持 Markdown 展示和 PDF 导出,支持零配置自动识别。
✨ 特性
- 🚀 轻量级:核心代码 < 500 行
- 🔒 类型安全:完整的 TypeScript 支持
- 🎨 美观:集成 Ant Design Vue
- 📦 灵活:支持组件和插件两种使用方式
- 💾 智能缓存:自动缓存已加载的文档
- 📄 PDF 导出:一键导出为 PDF 文件
- 🖨️ 打印支持:优化的打印样式
- 🔧 可配置:丰富的配置选项和插槽
- 🛡️ 安全:XSS 防护(DOMPurify)
📦 安装
npm install vue-doc-readme
# 或
pnpm add vue-doc-readme
# 或
yarn add vue-doc-readmePeer Dependencies:
{
"vue": ">=3.0.10",
"ant-design-vue": ">=2.1.3"
}🚀 快速开始
方式一:零配置模式(最简单,推荐)⭐
完全零配置! 组件会自动从URL路径最后一段获取报表ID:
<template>
<div>
<h1>日报统计</h1>
<!-- 自动从URL获取ID,例如 /system/one/report1 → report1 -->
<ReportReadmeButton />
</div>
</template>
<script setup lang="ts">
import { ReportReadmeButton } from 'vue-doc-readme'
import 'vue-doc-readme/style.css'
</script>URL示例:
/system/one/report1→ 自动获取report1/reports/daily/analytics→ 自动获取analytics/admin/report-123→ 自动获取report-123
当然,你也可以手动指定:
<template>
<!-- 手动指定ID(优先级更高) -->
<ReportReadmeButton report-id="custom-report-001" />
</template>就这么简单! 🎉
方式二:直接传入Markdown内容
如果你已经有markdown字符串,无需调用API:
<template>
<ReportReadmeButton :markdown="mdContent" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ReportReadmeButton } from 'vue-doc-readme'
import 'vue-doc-readme/style.css'
const mdContent = ref(`
# 使用说明
## 概述
这是报表的使用说明...
## 功能特点
1. 数据统计
2. 图表展示
3. 导出功能
`)
</script>优先级:markdown prop > API获取
方式三:插件模式(推荐用于全局配置)
基础配置
// main.ts
import { createApp } from 'vue'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
import ReportReadmePlugin from 'vue-doc-readme'
import 'vue-doc-readme/style.css'
import App from './App.vue'
const app = createApp(App)
app.use(Antd)
app.use(ReportReadmePlugin, {
apiUrl: '/api/report/doc', // 默认API地址
cacheDuration: 1800000, // 缓存30分钟
showExportBtn: true, // 显示导出按钮
showPrintBtn: true // 显示打印按钮
})
app.mount('#app')集成宿主系统的请求配置 ⭐
如果你的项目有统一的请求配置(如 utils/request.js),可以传入自定义请求函数:
// main.ts
import { createApp } from 'vue'
import ReportReadmePlugin from 'vue-doc-readme'
import 'vue-doc-readme/style.css'
import request from './utils/request' // 你的请求工具
import App from './App.vue'
const app = createApp(App)
app.use(ReportReadmePlugin, {
apiUrl: '/api/report/doc',
// 使用宿主系统的request(会自动带上baseURL、拦截器等配置)
requestFn: async (url, options) => {
const response = await request({
url,
method: options?.method || 'GET',
headers: options?.headers
})
return response.data // 返回格式: { resultData, resultStatus, code }
}
})
app.mount('#app')示例:集成axios
// utils/request.js
import axios from 'axios'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
// 请求拦截器
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
}
)
// 响应拦截器
service.interceptors.response.use(
response => response.data,
error => Promise.reject(error)
)
export default service// main.ts
import ReportReadmePlugin from 'vue-doc-readme'
import request from './utils/request'
app.use(ReportReadmePlugin, {
requestFn: async (url) => {
// request会自动带上baseURL、token等配置
return await request.get(url)
}
})好处:
- ✅ 统一使用项目的请求配置(baseURL、超时、拦截器)
- ✅ 自动带上token等认证信息
- ✅ 统一错误处理
- ✅ 复用现有请求工具
然后在任意组件中使用:
<template>
<ReportReadme report-id="daily-report-001" />
</template>方式四:弹窗模式
手动控制弹窗的显示和隐藏:
<template>
<div>
<a-button @click="showModal">查看说明</a-button>
<ReportReadmeModal
v-model:open="modalVisible"
report-id="daily-report-001"
modal-title="日报使用说明"
:modal-width="900"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ReportReadmeModal } from 'vue-doc-readme'
import '@your-org/report-readme/style.css'
const modalVisible = ref(false)
const showModal = () => {
modalVisible.value = true
}
</script>方式五:内嵌组件模式
直接在页面中展示(不使用弹窗):
<template>
<ReportReadme
report-id="daily-report-001"
api-url="/api/report/doc"
:auto-load="true"
:cache="true"
@load="handleLoad"
@error="handleError"
/>
</template>
<script setup lang="ts">
import { ReportReadme } from 'vue-doc-readme'
import '@your-org/report-readme/style.css'
import type { ReportDoc } from '@your-org/report-readme'
const handleLoad = (doc: ReportDoc) => {
console.log('加载成功:', doc)
}
const handleError = (error: Error) => {
console.error('加载失败:', error)
}
</script>📝 API
ReportReadmeButton(快捷按钮组件)
最简单的使用方式,一个按钮搞定一切。
Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| reportId | 报表ID(必需) | string | - |
| buttonText | 按钮文字 | string | 使用说明 |
| buttonType | 按钮类型 | 'default' \| 'primary' \| 'dashed' \| 'link' \| 'text' | default |
| buttonSize | 按钮尺寸 | 'large' \| 'middle' \| 'small' | middle |
| buttonIcon | 自定义按钮图标 | Component | QuestionCircleOutlined |
| modalTitle | 弹窗标题 | string | 报表说明 |
| modalWidth | 弹窗宽度 | string \| number | 900 |
| apiUrl | API地址 | string | /api/report/doc |
| autoLoad | 是否自动加载 | boolean | true |
| cache | 是否启用缓存 | boolean | true |
| cacheDuration | 缓存时长(毫秒) | number | 1800000 (30分钟) |
| showExportBtn | 是否显示导出按钮 | boolean | true |
| showPrintBtn | 是否显示打印按钮 | boolean | true |
| headers | 自定义请求头 | Record<string, string> | {} |
Events
| 事件 | 说明 | 回调参数 |
| --- | --- | --- |
| click | 点击按钮 | () => void |
| load | 加载成功 | (doc: ReportDoc) => void |
| error | 加载失败 | (error: Error) => void |
| exportSuccess | 导出成功 | (filename: string) => void |
| exportError | 导出失败 | (error: Error) => void |
使用示例
<template>
<!-- 零配置:自动从URL获取ID -->
<ReportReadmeButton />
<!-- 手动指定ID -->
<ReportReadmeButton report-id="daily-report-001" />
<!-- 自定义样式(同时自动获取ID) -->
<ReportReadmeButton
button-text="帮助文档"
button-type="primary"
button-size="small"
modal-title="日报使用指南"
:modal-width="1000"
/>
<!-- 监听事件 -->
<ReportReadmeButton
@click="handleClick"
@load="handleLoad"
@error="handleError"
/>
</template>工作原理
// URL: /system/reports/daily-report-001
<ReportReadmeButton />
// 等同于
<ReportReadmeButton report-id="daily-report-001" />
// 如果手动指定,会优先使用指定的ID
<ReportReadmeButton report-id="custom-id" />
// 使用 custom-id,忽略URLReportReadmeModal(弹窗组件)
手动控制弹窗的显示隐藏。
Props
继承 ReportReadme 的所有Props,额外增加:
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| open | 是否显示弹窗(v-model) | boolean | false |
| modalTitle | 弹窗标题 | string | 报表说明 |
| modalWidth | 弹窗宽度 | string \| number | 900 |
Events
继承 ReportReadme 的所有Events,额外增加:
| 事件 | 说明 | 回调参数 |
| --- | --- | --- |
| update:open | 弹窗显示状态变化 | (value: boolean) => void |
ReportReadme(基础组件)
内嵌展示模式,直接在页面中渲染。
Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| reportId | 报表ID(必需) | string | - |
| apiUrl | API地址 | string | /api/report/doc |
| autoLoad | 是否自动加载 | boolean | true |
| cache | 是否启用缓存 | boolean | true |
| cacheDuration | 缓存时长(毫秒) | number | 1800000 (30分钟) |
| showExportBtn | 是否显示导出按钮 | boolean | true |
| showPrintBtn | 是否显示打印按钮 | boolean | true |
| headers | 自定义请求头 | Record<string, string> | {} |
Events
| 事件 | 说明 | 回调参数 |
| --- | --- | --- |
| load | 加载成功 | (doc: ReportDoc) => void |
| error | 加载失败 | (error: Error) => void |
| exportSuccess | 导出成功 | (filename: string) => void |
| exportError | 导出失败 | (error: Error) => void |
Slots
| 插槽 | 说明 |
| --- | --- |
| header | 自定义头部 |
| loading | 自定义加载状态 |
| error | 自定义错误显示 |
| footer | 自定义底部 |
🎨 高级用法
快捷按钮自定义
<template>
<!-- 自定义按钮样式 -->
<ReportReadmeButton
report-id="daily-report-001"
button-text="查看帮助"
button-type="primary"
button-size="large"
/>
<!-- 自定义弹窗内容 -->
<ReportReadmeButton report-id="daily-report-001">
<template #header>
<div class="custom-header">
<a-tag color="blue">最新版本 v2.0</a-tag>
</div>
</template>
<template #footer>
<a-divider />
<p>有疑问?联系客服:400-xxx-xxxx</p>
</template>
</ReportReadmeButton>
</template>自定义加载和错误状态
<template>
<ReportReadme report-id="daily-report-001">
<template #loading>
<div class="custom-loading">
<a-spin size="large" />
<p>正在加载报表说明...</p>
</div>
</template>
<template #error="{ error }">
<a-alert
type="error"
:message="error.message"
show-icon
/>
</template>
</ReportReadme>
</template>自定义头部和底部
<template>
<ReportReadme report-id="daily-report-001">
<template #header>
<div class="custom-header">
<a-tag color="blue">最新版本</a-tag>
<span>更新时间: 2026-01-21</span>
</div>
</template>
<template #footer>
<a-divider />
<p class="footer-text">
如有疑问,请联系技术支持
</p>
</template>
</ReportReadme>
</template>使用 Composables
<script setup lang="ts">
import { ref } from 'vue'
import { useReportDoc, usePdfExport } from 'vue-doc-readme'
const reportId = ref('daily-report-001')
// 数据获取
const {
data,
isLoading,
isError,
error,
reload
} = useReportDoc({
reportId,
apiUrl: '/api/report/doc',
autoLoad: true,
cache: true
})
// PDF导出
const { isExporting, exportToPdf } = usePdfExport()
const handleExport = async () => {
const element = document.querySelector('.content')
if (element) {
await exportToPdf(element as HTMLElement, {
filename: 'report',
orientation: 'portrait'
})
}
}
</script>动态切换报表
<template>
<div>
<a-space>
<span>选择报表:</span>
<a-select v-model:value="currentReportId" style="width: 200px">
<a-select-option value="report-001">日报</a-select-option>
<a-select-option value="report-002">周报</a-select-option>
<a-select-option value="report-003">月报</a-select-option>
</a-select>
<!-- 按钮会自动跟随reportId变化 -->
<ReportReadmeButton :report-id="currentReportId" />
</a-space>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ReportReadmeButton } from 'vue-doc-readme'
const currentReportId = ref('report-001')
</script>在表格每行添加说明按钮
<template>
<a-table :columns="columns" :data-source="data">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<ReportReadmeButton
:report-id="record.reportId"
button-text="查看说明"
button-type="link"
button-size="small"
/>
</template>
</template>
</a-table>
</template>
<script setup lang="ts">
import { ReportReadmeButton } from 'vue-doc-readme'
const columns = [
{ title: '报表名称', dataIndex: 'name', key: 'name' },
{ title: '类型', dataIndex: 'type', key: 'type' },
{ title: '操作', key: 'action' }
]
const data = [
{ reportId: 'daily-001', name: '日报', type: '统计' },
{ reportId: 'weekly-001', name: '周报', type: '汇总' },
{ reportId: 'monthly-001', name: '月报', type: '分析' }
]
</script>🔌 后端接口规范
请求
GET /api/report/doc?reportId=xxx响应格式
{
resultStatus: 200 | '200', // 200或'200'表示成功
msg?: string, // 响应消息(可选)
resultData: {
id: string,
title: string,
content: string, // Markdown格式
version?: string,
updateTime?: string,
author?: string,
tags?: string[]
}
}说明:
resultStatus:200 或 '200' 字符串都表示成功msg:可选的消息字段,失败时包含错误信息resultData:可以是对象、数组或字符串
示例
成功响应:
{
"resultStatus": 200,
"msg": "success",
"resultData": {
"id": "daily-report-001",
"title": "日报使用说明",
"content": "# 日报使用说明\n\n## 1. 概述\n\n本报表用于...",
"version": "v1.0.0",
"updateTime": "2026-01-21T10:00:00Z",
"author": "系统管理员",
"tags": ["日报", "统计"]
}
}或
{
"resultStatus": "200",
"resultData": {
"id": "daily-report-001",
"title": "日报使用说明",
"content": "# 日报使用说明..."
}
}失败响应:
{
"resultStatus": 500,
"msg": "报表不存在"
}🎯 TypeScript 支持
完整的类型定义:
import type {
ApiResponse,
ReportDoc,
ReportReadmeProps,
ReportReadmePluginOptions,
PdfExportOptions
} from 'vue-doc-readme'
// 使用类型
const props: ReportReadmeProps = {
reportId: 'report-001',
apiUrl: '/api/report/doc',
autoLoad: true
}🔧 缓存管理
import { cacheManager } from 'vue-doc-readme'
// 清除特定缓存
cacheManager.delete('report-doc:report-001')
// 清除所有缓存
cacheManager.clear()
// 检查缓存
const hasCache = cacheManager.has('report-doc:report-001')
// 手动设置缓存
cacheManager.set('custom-key', data, 30 * 60 * 1000)📱 移动端适配
组件已针对移动端进行优化:
- 响应式布局
- 触摸友好的按钮尺寸
- 优化的字体大小
- 移动端打印支持
🖨️ 打印样式
打印时自动隐藏工具栏,优化内容显示:
@media print {
.report-readme__toolbar {
display: none;
}
}🛡️ 安全性
- XSS防护:使用 DOMPurify 清理 HTML
- CSP友好:不使用内联脚本
- 类型安全:完整的 TypeScript 类型检查
📊 性能优化
- ⚡ 智能缓存:避免重复请求
- 🎯 按需加载:懒加载 markdown 渲染库
- 🗜️ 体积优化:Tree-shaking 友好
- 🔄 虚拟滚动:适用于超长文档(可扩展)
🤝 兼容性
- Vue 3.3+
- Ant Design Vue 4.0+
- 现代浏览器(Chrome, Firefox, Safari, Edge)
- Node.js 16+
📄 License
MIT
🙋 常见问题
Q: 如何自定义 Markdown 样式?
A: 可以覆盖 .markdown-body 相关的 CSS 类:
.markdown-body h1 {
color: #1890ff;
}Q: 如何处理大文件?
A: 建议后端进行分页或截断,前端可以添加"加载更多"功能。
Q: 支持暗黑模式吗?
A: 当前版本不支持,可以通过 CSS 变量自定义主题色。
Q: PDF 导出图片模糊?
A: 可以调整 imageQuality 参数:
await exportToPdf(element, {
imageQuality: 1.0 // 最高质量
})