辣评 v0.9.0 大版本冲刺:暗黑模式、欠债重构与全面打磨(二十九)

 

三天时间,60+ 次提交,版本号从 v0.1.0-beta 跳到 v0.9.0。这是辣评平台到目前为止最密集的一次开发冲刺,涵盖了暗黑模式全量适配、欠债系统 v3 重构、管理界面大规模优化、文案提示系统、审计日志增强、用户手册重写等多个维度。本文记录这三天的核心工作和踩过的坑。


一、暗黑模式:从”能用”到”全量可用”

暗黑模式是这次冲刺中工作量最大的部分。Element Plus 自带暗黑主题支持,但实际落地远没有想象中简单——我们经历了五轮修复才彻底解决问题。

1.1 管理后台:CSS 变量缺失

第一轮修复管理后台。问题很直接:侧边栏、仪表盘、设置页等组件中有大量硬编码的白色背景色。

解决方案是新建 admin-tokens.css,定义暗黑模式下的 CSS 变量:

/* admin-vue/src/theme/admin-tokens.css */
html.dark {
  --app-bg: #141414;
  --app-surface: #1d1e1f;
  --app-surface-alt: #262727;
  --app-text: #e5eaf3;
  --app-text-secondary: #a3a6ad;
  --app-border: #414243;
  --el-card-bg-color: var(--app-surface);
}

然后逐页将 background: #fff 替换为 background: var(--app-surface)。管理后台涉及 6 个组件,替换了 8 处硬编码。

1.2 前台页面:22 处硬编码白色背景

管理后台修完后,发现前台页面(排行榜、评论、投稿等)同样有大量白色背景。这次用相同策略,新建 front-tokens.css 并批量替换,一次处理了 10 个文件中的 22 处硬编码。

1.3 根本性问题:scoped 样式与 html.dark 选择器

前两轮修完后仍然有页面暗黑模式不生效。排查发现了一个根本性问题

<!-- ❌ 这样写不生效! -->
<style scoped>
html.dark .my-component {
  background: #1d1e1f;
}
</style>

Vue 的 <style scoped> 会给选择器加上 data-v-xxx 属性限定,导致 html.dark 这种向上跨组件的选择器完全失效。

解决方案:将暗黑模式相关样式从 <style scoped> 中提取到单独的非 scoped <style> 标签中:

<style scoped>
/* 组件常规样式 */
.ranking-card { background: var(--app-surface); }
</style>

<!-- 暗黑模式覆盖必须放在非 scoped 的 style 中 -->
<style>
html.dark .ranking-card { background: var(--app-surface-alt); }
</style>

这次修复涉及排行榜、评论列表、参加比赛、添加评论 4 个核心页面。

1.4 深度修复:ECharts 图表与边角组件

还有 ECharts 统计图表在暗黑模式下文字看不清的问题。通过监听主题切换事件,动态更新图表配色:

// 监听暗黑模式切换
const isDark = useDark()
watch(isDark, () => {
  chart.setOption({
    textStyle: { color: isDark.value ? '#e5eaf3' : '#303133' },
    legend: { textStyle: { color: isDark.value ? '#e5eaf3' : '#606266' } }
  })
})

最终统计:暗黑模式适配共修复 60+ 处样式问题,覆盖全部前台和后台页面。


二、欠债补评论系统 v3 重构

2.1 从三维度到单维度

v2 的欠债系统使用三个维度(等效评论数、实际评论数、字数)来计算欠债,导致规则复杂且用户难以理解。v3 重构为单维度等效评论模型,大幅简化:

  • 用户只需要关注一个数字:欠多少条等效评论
  • 惩罚规则简化:零评论罚评数、单笔欠债上限、最大禁投届数三个参数可在后台配置
  • 前端 DebtManagement.vue 从三列展示改为单列,表头增加帮助 tooltip 解释计算规则

2.2 配置缺失问题

重构后发现后端 InitDefaultSettings 中缺少调度器相关的默认配置,导致欠债补评任务无法正常触发。同时 debt_count 列已被移除但查询代码中仍在引用。两个问题一起修复:

// cmd/server/database/database.go - 补充默认配置
func InitDefaultSettings() {
    defaults := map[string]string{
        "debt_zero_comment_penalty": "2",
        "debt_max_per_entry":        "5",
        "debt_max_ban_periods":      "3",
        // ...调度器配置
    }
    for key, val := range defaults {
        database.DB.FirstOrCreate(&models.Settings{}, models.Settings{Key: key, Value: val})
    }
}

所有测试文件也同步适配了 v3 单维度模型。


三、管理界面全面优化

这次冲刺对管理后台几乎所有页面做了一轮系统性优化。

3.1 侧边栏重组

原来的侧边栏菜单项扁平排列,改为四组分类:概览、内容管理、数据与规则、系统。每个菜单项使用唯一图标,整体视觉更清晰:

<!-- 分组式侧边栏 -->
<el-menu-item-group title="概览">
  <el-menu-item index="/admin/dashboard"><el-icon><Odometer /></el-icon>仪表盘</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="内容管理">
  <el-menu-item index="/admin/comments">...</el-menu-item>
  <el-menu-item index="/admin/submissions">...</el-menu-item>
</el-menu-item-group>

3.2 操作列瘦身

用户管理和比赛管理页面的操作列原来有多个按钮并排,占用 280-350px 宽度。改为「编辑 + 更多下拉菜单」模式,宽度降到 160px:

<el-table-column label="操作" width="160" fixed="right">
  <template #default="{ row }">
    <el-button size="small" @click="editUser(row)">编辑</el-button>
    <el-dropdown>
      <el-button size="small">更多<el-icon><ArrowDown /></el-icon></el-button>
      <template #dropdown>
        <el-dropdown-menu>
          <el-dropdown-item @click="resetPassword(row)">重置密码</el-dropdown-item>
          <el-dropdown-item @click="toggleBan(row)"></el-dropdown-item>
        </el-dropdown-menu>
      </template>
    </el-dropdown>
  </template>
</el-table-column>

3.3 系统设置页面重构

系统设置页从单栏改为双栏布局,Tab 合并调度器设置,统一保存按钮和全局 loading 状态。同时新增欠债惩罚参数的可视化配置,并补充了 20 个 Playwright E2E 测试用例。

3.4 仪表盘精简

仪表盘清理了 230 行旧代码(包括不再使用的图表和统计项),参赛管理 Tab 的 tooltip 改为动态读取后端配置。


四、文案提示系统

4.1 问题背景

辣评有大量业务规则(等效评论公式、合格条件、欠债惩罚规则等),之前这些文案散落在各个组件中,既难以维护也容易出现不一致。

4.2 集中管理方案

创建 helpTexts.ts 配置文件和 useHelpTexts composable:

// admin-vue/src/config/helpTexts.ts
export const helpTexts = {
  qualification: {
    formula: '等效评论 = 短篇评论×1 + 中篇评论×1.5 + 长篇评论×2',
    condition: '等效评论 ≥ {minEquivalent} 且 实际评论 ≥ {minActual} 且 总字数 ≥ {minWords}',
    tooltip: '合格条件中的阈值可在系统设置中配置'
  },
  debt: {
    penalty: '零评论额外罚 {zeroPenalty} 条,单笔上限 {maxPerEntry} 条',
    ban: '连续欠债 {maxBanPeriods} 届触发禁投'
  }
  // ...共 20 处文案
}
// admin-vue/src/composables/useHelpTexts.ts
export function useHelpTexts(section: string) {
  const settings = useSettingsStore()
  // 动态替换 {minEquivalent} 等占位符为后端配置值
  return computed(() => interpolate(helpTexts[section], settings.config))
}

最终接入了 6 个页面、13 处文案提示,覆盖了所有 P2/P3 级别的业务规则说明。


五、审计日志增强

5.1 覆盖面扩展

原来审计日志只记录 8 种操作类型。这次补充了 15 处写操作的审计记录,覆盖比赛管理、规则管理、评论操作、投稿操作、系统配置、调度器启停等,操作类型扩展到 27 种。

5.2 CSV 导出

新增导出接口,支持按当前筛选条件导出,输出 UTF-8 BOM 编码确保 Excel 直接打开不乱码:

// cmd/server/handlers/audit_handler.go
func ExportAuditLogs(c *gin.Context) {
    // ... 筛选逻辑同列表接口
    c.Header("Content-Type", "text/csv; charset=utf-8")
    c.Header("Content-Disposition", "attachment; filename=audit-logs.csv")
    // 写入 BOM
    c.Writer.Write([]byte{0xEF, 0xBB, 0xBF})
    writer := csv.NewWriter(c.Writer)
    writer.Write([]string{"时间", "操作人", "操作类型", "操作对象", "详情"})
    // ...
}

5.3 前端页面优化

审计日志页面做了 5 项改进:操作类型全中文化、分组筛选下拉、JSON 详情格式化展示、操作对象可读化(从 user:123 变为显示用户名)、增加导出按钮。


六、用户手册 v2.0

6.1 全面重写

原有的 10 章英文手册全部替换为 13 章中文文档,按照实际功能模块重新组织:

  1. 系统概述
  2. 注册与登录
  3. 比赛与投稿
  4. 评论与评分
  5. 排行榜与统计
  6. 任务与资格
  7. 用户管理
  8. 规则管理
  9. 投稿管理
  10. 欠债管理
  11. 系统设置
  12. 审计日志
  13. 常见问题

新增了排行榜、任务追踪、审计日志三个之前缺失的章节,并补充了暗黑模式的适配样式。


七、星级评分组件优化

评论打分从原来的自由数字输入改为 1-5 星评分,带渐变色和文字描述:

<el-rate
  v-model="form.rating"
  :texts="['很差', '较差', '一般', '较好', '很好']"
  :colors="['#F56C6C', '#E6A23C', '#909399', '#67C23A', '#409EFF']"
  show-text
  :allow-half="false"
/>

代评弹窗中的评分组件也同步升级,保持前后台交互一致。


八、工程化改进

8.1 版本号单一来源

之前版本号分散在 package.json.envconfig/index.ts 三处,容易出现不一致。重构为 version.json 单一来源:

{
  "version": "0.9.0",
  "versionName": "辣评",
  "buildDate": "2026-03-29"
}

构建时通过 prebuild 脚本自动同步到 package.json

// admin-vue/scripts/sync-version.js
const version = require('../version.json')
const pkg = require('../package.json')
pkg.version = version.version
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2))

8.2 部署脚本增强

部署脚本新增用户手册上传步骤,确保文档与代码同步部署。

8.3 CHANGELOG

正式建立版本变更日志,记录从 v0.1.0 到 v0.9.0 的所有功能变更,遵循语义化版本规范。


九、其他修复与优化

  • 评论管理不加载数据onMounted 中调用了未定义的 loadFilters() 导致崩溃,直接移除该调用
  • 投稿管理无默认届次:新增 getActiveCompetition 接口,页面加载时自动选中最新一届
  • 禁赛规则调整:禁赛用户仅限制投稿,不再限制登录和评论(用户体验改善)
  • 个人中心 15 项改进:统计项增加 tooltip、布局响应式优化、密码修改交互优化等
  • 测试数据种子脚本:生成 15 个覆盖各种业务场景的测试账号,方便开发调试
  • 移除前台全局搜索:功能使用率极低且增加维护负担,清理掉

总结

  1. 暗黑模式的教训:Vue scoped 样式中不能使用 html.dark 这类跨组件选择器,这是一个很容易忽略的陷阱。建议一开始就用 CSS 变量方案,避免在每个组件中重复写暗黑覆盖样式。

  2. 业务规则文案集中管理:当系统中有大量业务规则需要向用户解释时,散落在各组件中的文案迟早会出问题。helpTexts + composable 的方案成本低、效果好。

  3. 欠债系统简化:三维度模型看起来更精确,但用户理解成本太高。单维度模型虽然信息量少了,但用户更容易做出正确的行为响应。产品设计上,简单可理解比精确但复杂更重要。

  4. 操作列下拉菜单:管理后台表格的操作列如果按钮超过 2 个,就应该用下拉菜单收纳。否则表格宽度会被操作列挤占,主要数据反而展示不全。

  5. 版本号管理:任何需要在多处引用的配置值都应该有唯一来源。分散维护版本号是技术债务,迟早会出不一致的问题。

这三天的冲刺让辣评平台从一个功能基本可用的状态提升到了交互体验和工程质量都相对成熟的 v0.9.0。接下来的重点将是稳定性测试和 v1.0 正式发布。

本文遵守 Attribution-NonCommercial 4.0 International 许可协议。 Attribution-NonCommercial 4.0 International