Lumin Admin Dashboard 概览页面升级
📌 概述
Dashboard 概览页面是后台管理系统的核心入口,提供站点运营数据的全局视图。本次升级从最初的 4 个统计卡片 + 2 个简单图表,扩展为 10 个统计卡片 + 7 个 ECharts 图表 + 5 个列表模块的完整数据看板。
本功能基于 Vue 3 + Element Plus + ECharts 5.6.0 实现,所有图表支持响应式布局和暗色主题。
一、统计卡片
1.1 卡片设计
从 4 个扩展到 10 个,覆盖站点核心运营指标:
| 指标 | 图标 | 颜色 | 数据来源 |
|---|---|---|---|
| 文章总数 | Document | 蓝色 #3699ff | 扫描 content/posts 目录 |
| 分类数 | FolderOpened | 绿色 #0bb783 | Hugo 分类 taxonomy |
| 标签数 | PriceTag | 橙色 #ffa800 | Hugo 标签 taxonomy |
| 总字数 | EditPen | 紫色 #8950fc | 累加所有文章字数 |
| 说说 | ChatDotRound | 粉色 #f64e60 | 扫描 moments 目录 |
| 运行天数 | Timer | 青色 #00bcd4 | hugo.toml 中的 since 字段 |
| 友情链接 | Link | 红色 #e91e63 | 扫描 friends 数据 |
| 独立页面 | Files | 青绿 #00c853 | 扫描 pages 目录 |
| 媒体文件 | PictureFilled | 靛蓝 #5c6bc0 | 扫描 static 目录 |
| 本月发文 | Calendar | 琥珀 #ff6d00 | 按月筛选文章数 |
1.2 后端实现
1// server.js - Dashboard API
2app.get('/api/dashboard', async (_req, res) => {
3 const stats = {
4 posts: allPosts.length,
5 categories: Object.keys(catMap).length,
6 tags: Object.keys(tagMap).length,
7 words: allPosts.reduce((s, p) => s + (p.wordCount || 0), 0),
8 moments: momentsList.length,
9 days: Math.floor((Date.now() - sinceDate) / 86400000),
10 friends: friendsCount,
11 pages: pagesCount,
12 media: mediaCount,
13 monthPosts: monthPostsCount
14 }
15})
每个卡片使用渐变色背景 + 白色图标,悬停时有轻微上浮动效。
二、数据可视化图表
2.1 七大图表
| 图表 | 类型 | ECharts 配置 |
|---|---|---|
| 月度发文趋势 | 折线图 + 面积填充 | areaStyle + 渐变色 |
| 内容构成 | 环形饼图 | radius: ['40%', '70%'] |
| 分类统计 | 柱状图 | 每个分类独立配色 |
| 文章状态 | 饼图 | 已发布/草稿/置顶/精选 |
| 发布时段 | 柱状图 | 24 小时分布 |
| 年度归档 | 横向柱状图 | 按年份统计 |
| 标签云 | 词云图 | echarts-wordcloud 插件 |
2.2 标签云兼容性问题
问题:标签云不显示,控制台无报错。
根因分析:
- echarts 版本不兼容:
echarts-wordcloud@2.1.0仅兼容echarts@5,与echarts@6不兼容 - 模块实例不一致:动态
await import('echarts')和echarts-wordcloud内部import * as echarts from 'echarts/lib/echarts'可能创建不同的模块实例,导致 wordcloud 注册到了另一个 echarts 实例
解决方案:
1// ❌ 之前:动态导入(模块实例不一致)
2const loadECharts = async () => {
3 const echarts = await import('echarts')
4 await import('echarts-wordcloud')
5 return echarts
6}
7
8// ✅ 修复:静态导入(确保同一实例)
9import * as echarts from 'echarts'
10import 'echarts-wordcloud'
11
12const loadECharts = () => echarts
同时将 package.json 中 echarts 版本从 ^6.1.0 降级到 ^5.0.0(实际安装 5.6.0)。
2.3 图表渐变色实现
所有图表使用 echarts.graphic.LinearGradient 实现渐变效果:
1areaStyle: {
2 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
3 { offset: 0, color: 'rgba(54, 153, 255, 0.3)' },
4 { offset: 1, color: 'rgba(54, 153, 255, 0.02)' }
5 ])
6}
三、列表模块
3.1 最近发布
显示 8 篇最新文章,分类显示中文名称而非英文 slug。
分类名称映射:
1// 后端构建 catTitleMap
2const catTitleMap = {}
3for (const [slug, posts] of Object.entries(categories)) {
4 if (posts.length) {
5 const fm = parseFrontMatter(readFileSync(posts[0], 'utf-8'))
6 catTitleMap[slug] = fm.title ? '' : slug
7 }
8}
9// 从 hugo.toml 的 [taxonomies] 和菜单配置中获取中文标题
每篇文章新增 categoryTitle 字段:
1recentPosts = allPosts.slice(0, 8).map(p => ({
2 ...p,
3 categoryTitle: catTitleMap[p.categories?.[0]] || p.categories?.[0] || ''
4}))
3.2 版本更新
从 themeVersion.md 读取版本历史数据,使用 YAML Front Matter 格式:
1---
2versions:
3 - version: "Beta 1.9"
4 date: "2026-05-20"
5 changes:
6 - "新增后台管理系统 Dashboard 概览页面"
7 - "新增文章 Front Matter 参数管理功能"
8---
使用 js-yaml 库解析自定义 versions 字段(parseFrontMatter 仅支持预定义字段):
1const fmMatch = verRaw.match(/^---\n([\s\S]*?)\n---/)
2if (fmMatch) {
3 const fmData = yaml.load(fmMatch[1])
4 versionHistory = fmData.versions.map(v => ({
5 version: v.version,
6 date: v.date,
7 changes: Array.isArray(v.changes) ? v.changes : []
8 }))
9}
3.3 站点信息
从 6 项扩展到 11 项:
| 信息项 | 数据来源 |
|---|---|
| 站点标题 | hugo.toml → title |
| 站点地址 | hugo.toml → baseurl |
| Hugo 版本 | execSync('hugo version') |
| 主题 | hugo.toml → theme |
| 主题版本 | hugo.toml → themeVersion |
| 语言 | hugo.toml → languageCode |
| 所在地 | hugo.toml → location |
| 组织 | hugo.toml → organization |
| 建站日期 | hugo.toml → since |
| 内容大小 | 扫描 content 目录 |
| 媒体大小 | 扫描 static 目录 |
3.4 高度统一
版本更新和站点信息面板使用 el-row 的 type="flex" align="stretch" 实现等高:
1<el-row :gutter="16" type="flex" align="stretch">
2 <el-col :xs="24" :lg="14">
3 <el-card class="version-card">...</el-card>
4 </el-col>
5 <el-col :xs="24" :lg="10">
6 <el-card class="info-card">...</el-card>
7 </el-col>
8</el-row>
1.version-card, .info-card {
2 height: 100%;
3}
四、TOML 配置字段名问题
parseHugoConfig 将 TOML 键名统一转小写存储,但 Dashboard API 使用的是原始大小写:
1// ❌ 之前:大小写不匹配
2cfg.baseURL // 实际存储为 cfg.baseurl
3cfg.defaultContentLanguage // 实际存储为 cfg.languageCode
4
5// ✅ 修复:兼容两种写法
6cfg.baseurl || cfg.baseURL
7cfg.languageCode || cfg.defaultContentLanguage
五、themeVersion 位置调整
将 themeVersion 从 [params.profile] 移到 hugo.toml 顶层基础信息区域:
1# 一、站点基础信息
2baseURL = 'https://example.com/'
3title = 'Lumin Blog'
4theme = 'lumin'
5themeVersion = 'Beta 1.9' # 从 [params.profile] 移到此处
前端设置页面同步调整,主题版本号从「个人资料」Tab 移到「基础信息」Tab。
六、文件变更清单
| 文件 | 变更 |
|---|---|
admin/frontend/src/views/Dashboard/index.vue | 全面重构,10 卡片 + 7 图表 + 5 列表 |
admin/backend/server.js | Dashboard API 改为 async,新增 versionHistory/siteInfo |
admin/frontend/package.json | echarts 降级到 ^5.0.0 |
admin/backend/package.json | 新增 js-yaml 依赖 |
myblog/hugo.toml | themeVersion 移到顶层 |
myblog/themeVersion.md | 新建版本历史数据文件 |
admin/frontend/src/views/Settings/Index.vue | 主题版本号移到基础信息 Tab |
留言评论
期待你的想法评论加载中