Files
gamegroup2/frontend/src/views/Changelog.vue
T
congsh a062889a11 fix: bug fixes and UX improvements (v0.3.5)
- Fix clipboard copy error in HTTP environment with execCommand fallback
- Fix team invite page not loading user groups, always showing "join group first"
- Fix JoinGroupPage isMember check using group object instead of user ID
- Fix cancelRSVP deleting all users' RSVP records instead of current user's
- Fix event detail not loading event data itself
- Fix event comment avatar URL missing PocketBase baseUrl prefix
- Fix event creation missing endTime > startTime validation
- Fix event manage/delete permission split (creator+owner vs creator+owner)
- Fix event create button only visible to admins, now all members can create
- Fix event expand not subscribing to comments/RSVP realtime updates
- Fix event relative time not using status field
- Remove duplicate create/join group buttons from header and welcome bar
- Refactor team invite link to use API function

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:19:18 +08:00

341 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 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>