<!-- src/views/Changelog.vue -->
<script setup lang="ts">
import { ref } from 'vue'

interface LogEntry {
  version: string
  date: string
  title: string
  items: { type: 'feat' | 'fix' | 'refactor' | 'style'; text: string }[]
}

const logs = ref<LogEntry[]>([
  {
    version: 'v0.3.5',
    date: '2026-04-21',
    title: 'Bug 修复与体验优化',
    items: [
      { type: 'fix', text: '修复邀请链接复制在 HTTP 环境下报错的问题，添加 execCommand 降级方案' },
      { type: 'fix', text: '修复小队邀请链接页面未加载用户群组数据，导致始终提示"需要先加入群组"' },
      { type: 'fix', text: '修复加入群组页面 isMember 判断错误，使用了群组对象而非用户 ID' },
      { type: 'fix', text: '修复取消 RSVP 时误删所有用户 RSVP 记录的问题' },
      { type: 'fix', text: '修复活动详情加载时未获取活动本身数据的问题' },
      { type: 'fix', text: '修复活动评论头像 URL 未拼接 PocketBase baseUrl 导致图片加载失败' },
      { type: 'fix', text: '修复活动创建未校验结束时间必须晚于开始时间' },
      { type: 'fix', text: '修复活动管理权限判断，拆分编辑权限（创建者+群主）和删除权限（创建者+群主）' },
      { type: 'fix', text: '修复活动发起按钮仅管理员可见，改为所有群组成员可发起' },
      { type: 'fix', text: '修复活动展开详情后未订阅评论和 RSVP 实时更新' },
      { type: 'fix', text: '修复活动相对时间未使用 status 字段判断状态的问题' },
      { type: 'refactor', text: '移除顶部 Header 和首页欢迎条中重复的"创建群组""加入群组"按钮' },
      { type: 'refactor', text: '小队邀请链接改用 API 函数替代原始 PocketBase 调用' },
    ]
  },
  {
    version: 'v0.3.4',
    date: '2026-04-21',
    title: '公告详情弹窗',
    items: [
      { type: 'feat', text: '公告卡片点击打开详情弹窗，展示完整内容（保留换行格式）' },
      { type: 'feat', text: '详情弹窗显示优先级标签、作者、发布时间、截止时间等信息' },
      { type: 'feat', text: '详情弹窗内置编辑和删除操作按钮' },
    ]
  },
  {
    version: 'v0.3.3',
    date: '2026-04-20',
    title: 'Electron 桌面客户端',
    items: [
      { type: 'feat', text: 'Electron 桌面端封装：将 Web 应用包装为独立桌面应用，支持 Windows/Linux/macOS' },
      { type: 'fix', text: '修复 HTTP 环境下 mediaDevices 不可用导致语音认证失败的问题' },
    ]
  },
  {
    version: 'v0.3.2',
    date: '2026-04-19',
    title: '实时语音房间',
    items: [
      { type: 'feat', text: '语音房间：组队中可进入独立语音房间，实时语音通话（基于 LiveKit WebRTC）' },
      { type: 'feat', text: '成员头像网格：显示在线成员，说话时绿圈呼吸动画提示' },
      { type: 'feat', text: '麦克风/扬声器开关：独立控制麦克风和扬声器' },
      { type: 'feat', text: 'LiveKit 后端服务：Docker 部署 LiveKit SFU + Token 签发微服务' },
      { type: 'fix', text: 'LiveKit 服务端升级到 v1.10 兼容客户端 v2 SDK' },
      { type: 'fix', text: '修复 WebRTC ICE 连接失败，配置 node-ip 为宿主机地址' },
    ]
  },
  {
    version: 'v0.3.1',
    date: '2026-04-19',
    title: '玩家黑名单',
    items: [
      { type: 'feat', text: '玩家黑名单：标记外部平台坑玩家（挂机、送人头、喷人、外挂等），记录玩家ID和游戏平台' },
      { type: 'feat', text: '玩家卡片聚合：按玩家ID+平台聚合展示，显示被标记次数和所有标签' },
      { type: 'feat', text: '展开详情：点击玩家卡片展开查看所有标记记录，含举报人、时间、严重程度' },
      { type: 'feat', text: '搜索筛选：按玩家ID搜索、按标签和严重程度筛选' },
      { type: 'feat', text: '黑名单页面 Tab 切换：游戏黑名单与玩家黑名单同一页面切换展示' },
      { type: 'feat', text: '自定义标签：除预定义标签外支持填写自定义标签' },
      { type: 'feat', text: '实时订阅：玩家黑名单变更实时更新，无需刷新页面' },
    ]
  },
  {
    version: 'v0.3.0',
    date: '2026-04-19',
    title: '四期功能：积分竞猜、游戏黑名单',
    items: [
      { type: 'feat', text: '积分竞猜：群组成员可发起竞猜，设置选项、截止时间、下注积分范围' },
      { type: 'feat', text: '竞猜下注：成员选择选项并下注积分，每人仅限下注一次' },
      { type: 'feat', text: '竞猜开奖：发起人关闭竞猜后选择正确答案，奖池按比例分配给赢家' },
      { type: 'feat', text: '竞猜自动关闭：到达截止时间自动关闭竞猜' },
      { type: 'feat', text: '游戏黑名单：标记体验差的游戏（外挂、挂机、环境差等），提醒队友避坑' },
      { type: 'feat', text: '黑名单筛选：按原因（队友坑/外挂多/坑货多/环境差）和严重程度筛选' },
      { type: 'feat', text: '黑名单详情展开：点击游戏卡片展开查看所有标记记录' },
      { type: 'feat', text: '竞猜列表展示奖池进度条和「已下注」状态标签' },
      { type: 'fix', text: '修复下注选项在 Chrome/Edge 无法选中的问题，改为按钮卡片式选择' },
      { type: 'fix', text: '修复 point_logs schema 不支持竞猜动作和负数积分的问题' },
      { type: 'fix', text: '修复竞猜双重结算和 TOCTOU 竞态安全漏洞' },
      { type: 'fix', text: '修复黑名单条目允许非创建者修改的安全问题' },
      { type: 'fix', text: '修复 BetDetail 订阅累积和群组切换时订阅未清理的内存泄漏' },
      { type: 'fix', text: '修复 GroupView 定时器变量覆盖导致内存泄漏' },
    ]
  },
  {
    version: 'v0.2.0',
    date: '2026-04-18',
    title: '三期功能：账目管理、资产管理',
    items: [
      { type: 'feat', text: '账目管理：群组内记录收入/支出流水，支持游戏/聚餐/设备/交通/其他分类' },
      { type: 'feat', text: '账目汇总面板：实时显示当月总收入、总支出、余额' },
      { type: 'feat', text: '账目筛选：按月份、类型（收入/支出）、分类多维筛选' },
      { type: 'feat', text: '资产管理：登记群组公共资产（游戏账号、主机、设备、配件等）' },
      { type: 'feat', text: '资产转移：成员可自由标记当前持有人，支持放回在库' },
      { type: 'feat', text: '资产图片上传：支持拍照上传资产照片，缩略图预览' },
      { type: 'feat', text: '群组导航新增"账目"和"资产"快捷入口' },
      { type: 'fix', text: '修复 games API 自动取消请求导致游戏库加载失败' },
      { type: 'fix', text: '修复 PocketBase SDK 并发请求 auto-cancellation 竞态问题' },
      { type: 'fix', text: '修复实时订阅未正确清理导致的内存泄漏' },
      { type: 'fix', text: '修复资产图片上传缺少服务端 MIME 类型校验' },
      { type: 'fix', text: '修复旧迁移文件阻塞新集合创建的问题' },
    ]
  },
  {
    version: 'v0.1.1',
    date: '2026-04-18',
    title: '二期功能优化',
    items: [
      { type: 'feat', text: '投票编辑：发起人可修改标题、选项、截止时间，已投票选项不可删除' },
      { type: 'feat', text: '通知面板展示站内消息通知，支持投票/组队/群组通知跳转' },
      { type: 'feat', text: '创建投票后自动通知同群组成员' },
      { type: 'feat', text: '邀请被拒绝/接受后通知邀请发起人' },
      { type: 'feat', text: '通知点击跳转到对应群组并切换到相关 Tab' },
      { type: 'fix', text: '修复通知 createRule 权限限制导致无法给他人发送通知' },
      { type: 'fix', text: '修复截止时间时区偏差，统一 ISO 格式存储' },
      { type: 'fix', text: '修复投票列表 auto-cancel 竞态和选项 order=0 校验失败' },
      { type: 'fix', text: '修复 nginx 静态缓存误拦截 API 文件请求和上传大小限制' },
      { type: 'fix', text: '修复回忆 Tab 图片不展示和视频无法播放问题' },
      { type: 'fix', text: '修复切换 Tab 数据不加载的时序问题' },
    ]
  },
  {
    version: 'v0.1.0',
    date: '2026-04-18',
    title: '二期功能：投票、回忆、统计',
    items: [
      { type: 'feat', text: '群组投票：支持选项投票和接龙报名两种模式，可设置截止时间、匿名投票' },
      { type: 'feat', text: '投票编辑：发起人可修改标题、选项、截止时间，已投票选项不可删除' },
      { type: 'feat', text: '投票结算：到达截止时间自动结算，发起人也可手动结束投票' },
      { type: 'feat', text: '多媒体回忆：群组内上传图片/视频/音频/文档，缩略图预览和弹窗播放' },
      { type: 'feat', text: '数据统计：群组内展示本周组队次数、投票参与率、积分排行' },
      { type: 'feat', text: '站内通知：新增投票、组队、入群等场景通知，铃铛图标显示未读数' },
      { type: 'feat', text: '积分体系：参与投票/组队/上传获取积分，群组内展示排行' },
      { type: 'feat', text: '群组详情页 Tab 重构：动态/投票/回忆/统计四个 Tab，带图标醒目展示' },
      { type: 'fix', text: '修复 nginx 代理文件上传 413 问题和静态资源误拦截 API 文件请求' },
      { type: 'fix', text: '修复投票列表 auto-cancel 竞态问题和选项排序 0 值校验失败' },
      { type: 'fix', text: '修复截止时间时区偏差，统一使用 ISO 格式存储' },
    ]
  },
  {
    version: 'v0.0.3',
    date: '2026-04-18',
    title: '优化登录和注册',
    items: [
      { type: 'feat', text: '支持中文昵称登录：输入昵称即可登录，无需记忆邮箱' },
      { type: 'feat', text: '注册时昵称唯一性检查：失焦自动检测，实时反馈是否可用' },
      { type: 'feat', text: '注册支持中文昵称，自动生成系统用户名' },
      { type: 'fix', text: '密码规则清晰展示：四项规则标签 + 密码强度指示条' },
    ]
  },
  {
    version: 'v0.0.2',
    date: '2026-04-18',
    title: '优化页面元素和游戏库筛选',
    items: [
      { type: 'style', text: '统一色彩体系，将混用的蓝/紫色调全部替换为绿色主题' },
      { type: 'feat', text: '侧边栏「创建群组」「加入群组」按钮添加文字标签，提升可发现性' },
      { type: 'feat', text: '顶部 Header 增加快捷操作入口（创建群组、加入群组、通知）' },
      { type: 'feat', text: '移动端适配：添加汉堡菜单，侧边栏滑动展开' },
      { type: 'feat', text: '首页欢迎条增加 CTA 按钮，无群组时显示引导卡片' },
      { type: 'feat', text: '加入群组弹窗新增按名称搜索模式，保留 ID 查找作为备选' },
      { type: 'feat', text: '游戏库页面内置群组下拉选择，不再依赖外部选择' },
      { type: 'fix', text: '热门游戏空状态优化提示文案' },
      { type: 'refactor', text: '首页无临时小组时自动折叠空闲成员区域' },
    ]
  },
  {
    version: 'v0.0.1',
    date: '2026-04-17',
    title: '项目初始化',
    items: [
      { type: 'feat', text: '搭建项目基础架构：Vue 3 + TypeScript + Pinia + Element Plus + Tailwind CSS' },
      { type: 'feat', text: '集成 PocketBase 后端服务，完成用户认证（注册/登录/Cookie 持久化）' },
      { type: 'feat', text: '实现群组管理：创建群组、加入群组（ID 查找）、解散群组' },
      { type: 'feat', text: '实现入群审批流程：审核开关、提交申请、群主审批' },
      { type: 'feat', text: '实现临时小组：创建小队、邀请成员、开始游戏、结束解散' },
      { type: 'feat', text: '实现组队邀请：发送邀请、接受/拒绝、实时通知' },
      { type: 'feat', text: '实现游戏库：添加游戏、导入导出、评论收藏、热门排行' },
      { type: 'feat', text: '用户状态管理：空闲/工作中/组队中/离开，工作时间自动切换' },
      { type: 'feat', text: '实时数据同步：PocketBase Realtime 订阅群组、成员、邀请变更' },
      { type: 'feat', text: 'Docker 部署方案：Dev/UAT 环境分离，独立 PocketBase 实例' },
    ]
  }
])

const typeMap: Record<string, { label: string; color: string }> = {
  feat: { label: '新功能', color: 'var(--gg-primary)' },
  fix: { label: '修复', color: 'var(--gg-warning)' },
  refactor: { label: '优化', color: 'var(--gg-info)' },
  style: { label: '样式', color: 'var(--gg-accent)' },
}
</script>

<template>
  <div class="changelog-page">
    <h1 class="page-title">更新日志</h1>

    <div class="timeline">
      <section v-for="log in logs" :key="log.version" class="version-block">
        <div class="version-header">
          <span class="version-tag">{{ log.version }}</span>
          <span class="version-date">{{ log.date }}</span>
        </div>
        <h2 class="version-title">{{ log.title }}</h2>
        <ul class="change-list">
          <li v-for="(item, i) in log.items" :key="i" class="change-item">
            <span class="change-type" :style="{ background: typeMap[item.type].color + '18', color: typeMap[item.type].color }">
              {{ typeMap[item.type].label }}
            </span>
            <span class="change-text">{{ item.text }}</span>
          </li>
        </ul>
      </section>
    </div>
  </div>
</template>

<style scoped>
.changelog-page {
  max-width: 680px;
  margin: 0 auto;
}

.page-title {
  font-size: 28px;
  font-weight: 700;
  margin: 0 0 28px;
  background: var(--gg-gradient);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.timeline {
  display: flex;
  flex-direction: column;
  gap: 28px;
  padding-left: 20px;
  border-left: 3px solid var(--gg-border);
}

.version-block {
  position: relative;
  padding: 20px 24px;
  background: var(--gg-bg-card);
  border: 1px solid var(--gg-border);
  border-radius: var(--gg-radius-lg);
  transition: border-color 0.2s;
}

.version-block:hover {
  border-color: var(--gg-primary);
}

.version-block::before {
  content: '';
  position: absolute;
  left: -29px;
  top: 28px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--gg-primary);
  border: 3px solid var(--gg-bg);
}

.version-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
}

.version-tag {
  display: inline-block;
  padding: 3px 12px;
  border-radius: 20px;
  background: var(--gg-gradient-green);
  color: white;
  font-size: 13px;
  font-weight: 600;
}

.version-date {
  font-size: 13px;
  color: var(--gg-text-muted);
}

.version-title {
  font-size: 18px;
  font-weight: 600;
  margin: 0 0 16px;
  color: var(--gg-text);
}

.change-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.change-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 14px;
  line-height: 1.5;
}

.change-type {
  flex-shrink: 0;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.6;
}

.change-text {
  color: var(--gg-text-secondary);
}
</style>
