26.1.26 新功能开发计划
背景
为了增强用户在打卡列表的互动性和管理能力,需要在打卡卡片(CheckinCard)上增加更多操作选项,包括置顶、评论、海报分享以及展示评论列表。这些功能将通过环境变量开关进行控制,以便按需开启。
需求拆解
- 置顶功能 (优先级: 🔴 高) - 允许教师将特定打卡内容置顶
- 评论功能 (优先级: 🔴 高) - 允许用户对打卡内容进行评论
- 评论列表 (优先级: 🔴 高) - 在打卡卡片下方显示该打卡的评论记录
- 海报功能 (优先级: 🟡 中) - 生成包含打卡内容的长图海报,支持分享
环境变量规划
在 .env 中增加以下开关(默认 '0' 关闭,'1' 开启):
-
VITE_FEATURE_CHECKIN_TOP=1(置顶功能) -
VITE_FEATURE_CHECKIN_COMMENT=1(评论功能) -
VITE_FEATURE_CHECKIN_POSTER=1(海报功能) -
VITE_FEATURE_CHECKIN_COMMENT_LIST=1(评论列表显示)
详细设计与逻辑流程
1. 入口改造 (CheckinCard)
位置: CheckinCard 组件底部右侧操作区(原点赞/更多按钮处)
交互: 点击"..."图标,从底部弹出 ActionSheet(Vant 组件)
菜单项:
-
置顶 (仅教师可见): 仅当
VITE_FEATURE_CHECKIN_TOP=1时显示。若已置顶,显示"取消置顶" -
评论 (仅用户端可见): 仅当
VITE_FEATURE_CHECKIN_COMMENT=1时显示,仅在/checkin/index打卡主页显示 -
海报 (通用): 仅当
VITE_FEATURE_CHECKIN_POSTER=1时显示
2. 置顶功能 🔴 高优先级
权限控制
-
仅教师可见: 使用
user.is_teacher字段判断权限 -
权限获取: 前端需实时请求后端接口获取用户角色,不依赖缓存的
localStorage.user_info - 适用范围: 教师端所有打卡列表页面
逻辑流程
- 点击"置顶"菜单
- 弹出确认框
showConfirmDialog:"确定要置顶这条打卡吗?" - 用户确认 -> 调用 API
teacherPinCheckinAPI(id) - API 成功 -> Toast "置顶成功" -> 更新列表数据(将该项标记为置顶)
- 若已置顶 -> 点击"取消置顶" -> 确认 -> API
teacherUnpinCheckinAPI(id)-> Toast "取消置顶成功" -> 更新状态
视觉标记
- 已置顶的打卡卡片右上角显示 📌 图标
- 卡片顶部显示"置顶"标签(使用
van-tag组件)
API 接口
-
POST /api/teacher/pin-checkin- 教师置顶打卡 (参数:checkin_id) -
POST /api/teacher/unpin-checkin- 教师取消置顶打卡 (参数:checkin_id)
数据字段扩展
{
is_pinned: false, // 是否置顶
pinned_at: null // 置顶时间
}
3. 评论功能 🔴 高优先级
权限控制
-
仅用户端可用: 只能在
/checkin/index打卡主页的打卡卡片上显示 - 允许自评: 允许用户评论自己的打卡
评论输入弹窗 (CheckinCommentDialog)
组件: CheckinCommentDialog.vue (基于 Vant Popup 封装,底部弹出)
功能特性:
- 文本输入框 (
van-field,多行模式) - 表情选择器按钮
- 字符计数提示(最多 200 字)
- 按钮: 取消、提交
表情选择器:
- 技术方案: 自定义 Emoji 面板,使用原生 Unicode Emoji
-
UI 实现:
van-popup底部弹出 +van-grid展示表情符号 - 常用表情: 😊😂❤️👍🎉✨🙏💪🔥💯😍👏🤝🌟🎁😭😡🤔💭😴🎂🌈⭐🌙☀️🌺🌸🍀🎁🎈🎵🎶📱💻⚽🏀🎾🎯🎨🎬📷🎤🎧📚✏️📝💡🔔💬📧📞
- 交互逻辑: 点击表情 -> 插入到输入框光标位置 -> 自动关闭面板
输入验证:
- 纯文本评论至少 5 个字符
- 允许纯表情评论(无文字)
- 字符计数使用
Array.from(text).length计算(正确处理 Emoji 代理对) - 前端实时提示剩余字数
API 接口
-
POST /api/checkin/comment- 发表评论- 参数:
checkin_id,content(评论内容,最多 200 字) - 返回:
{ code: 1, data: { comment_id: 123 }, msg: '' }
- 参数:
- 无需敏感词过滤: 后端不进行敏感词过滤
通知机制
- 评论后不需要前端主动触发通知
- 后端会自动发送通知给打卡用户或被回复用户
4. 评论列表功能 🔴 高优先级
位置与显示
位置: CheckinCard 组件内部下方
显示条件: VITE_FEATURE_CHECKIN_COMMENT_LIST=1 且 post.comments.length > 0
评论列表展示 (CheckinCommentList)
组件: CheckinCommentList.vue (参考 StudyCommentsSection.vue 的实现模式)
显示逻辑:
- 首屏最多展示 5 条评论
- 超过 5 条显示"查看全部"按钮
- 点击"查看全部"加载完整评论列表(懒加载)
- 样式参考微信朋友圈(灰色背景,每行
用户: 内容)
数据结构 (扁平化设计):
{
id: 1, // 评论 ID
checkin_id: 123, // 关联的打卡 ID
user_id: 456, // 评论者 ID
user_name: "张三", // 评论者昵称
content: "干得漂亮!🎉", // 评论内容
parent_id: 0, // 父评论 ID (0 表示一级评论)
reply_to_user_id: null, // 被回复用户 ID (一级评论为 null)
reply_to_user_name: null, // 被回复用户昵称
is_my: false, // 是否是自己发表的评论
created_at: "2026-01-26 10:00:00"
}
评论样式:
- 一级评论:
张三: 干得漂亮!🎉 - 回复评论:
张三 回复 李四: 我也这么觉得! - Emoji 符号直接使用原生 Unicode 渲染
交互功能
回复评论:
- 点击某一条评论,从屏幕底部弹出输入框(类似微信朋友圈)
- 键盘自动升起
- 输入框同样支持表情选择器
- 输入内容后点击发送,即为回复该条评论
- 数据结构: 使用
parent_id标识父评论,reply_to_user_id标识被回复用户 - 显示样式:
用户A 回复 用户B: 评论内容
删除评论:
- 仅显示用户自己发布的评论的删除图标
- 点击图标弹出确认框:"确定要删除这条评论吗?"
- 确认后调用删除接口移除该评论
- API:
DELETE /api/checkin/comment/:id -
权限校验:
- 前端: 通过接口返回的
is_my字段判断是否显示删除按钮 - 后端: 接口层进行权限校验,确保只能删除自己的评论
- 前端: 通过接口返回的
- 教师权限: 教师不可删除学生的评论
教师评论管理 🔴 高优先级 (新增需求)
适用场景: 教师在 /teacher 路由下的打卡列表中管理评论
功能:
- 教师可以在评论列表中对不适当的评论进行审核
- 操作选项:
- 屏蔽: 隐藏该评论(不删除,仅前端不显示)
- 通过: 恢复该评论的显示
- 评论状态字段:
status(0= 待审核,1= 已通过,2= 已屏蔽) -
交互设计:
- 在评论卡片右侧显示审核按钮(仅教师可见)
- 点击按钮弹出 ActionSheet: "屏蔽评论"、"通过评论"
- 操作后实时更新评论状态
API 接口 (新增):
-
POST /api/teacher/review-comment- 教师审核评论- 参数:
comment_id,action(approve|reject) - 返回:
{ code: 1, data: {}, msg: '' }
- 参数:
数据字段扩展:
{
status: 1, // 评论状态: 0-待审核, 1-已通过, 2-已屏蔽
reviewed_at: null, // 审核时间
reviewed_by: null // 审核人 ID
}
API 接口
获取评论列表:
GET /api/checkin/comments- 参数:
checkin_id,page,page_size - 返回:
{ code: 1, data: { list: [], total: 100 }, msg: '' } -
注意: 不包含完整的用户信息,仅返回
user_id和user_name
发表评论:
POST /api/checkin/comment- 参数:
checkin_id,content - 返回:
{ code: 1, data: { comment_id: 123 }, msg: '' }
回复评论:
POST /api/checkin/comment/reply- 参数:
checkin_id,content,parent_id,reply_to_user_id - 返回:
{ code: 1, data: { comment_id: 124 }, msg: '' }
删除评论:
DELETE /api/checkin/comment/:id- 参数:
comment_id - 返回:
{ code: 1, data: {}, msg: '' }
数据字段扩展
{
comments_count: 0, // 评论总数(用于列表展示)
comments: [] // 评论列表(最多 5 条,用于首屏渲染)
}
5. 海报功能 🟡 中优先级
逻辑流程
- 点击"海报"菜单
- 弹出 海报预览组件 (
CheckinPoster,参考SharePoster) -
渲染内容:
- 用户信息(头像、昵称)
- 打卡内容(文本、图片缩略图)
- 底部二维码(打卡主页的网址自动生成)
- 支持长图,若内容过长通过滚动查看
-
生成图片: 使用
html2canvas或html-to-image将 DOM 转为图片 - 展示生成的图片,提示"长按保存"
降级方案
- 海报生成是异步操作,可能会失败
- 生成失败时显示降级卡片:
- 卡片包含: 用户头像、昵称、打卡内容摘要
- 底部提示: "长按截图保存分享"
- 用户可手动截图保存
技术要点
- 处理跨域图片: 配置
useCORS: true且 CDN 支持 CORS - 长图渲染: 需限制最大高度或分段(第一版暂不考虑极端情况)
- 字体加载: 确保字体加载完成后再生成图片
开发步骤
🔴 第 1 阶段: 置顶功能 (高优先级)
目标: 实现教师置顶打卡功能
步骤:
-
环境准备:
- 在
.env添加VITE_FEATURE_CHECKIN_TOP=1 - 在
src/api/teacher.js中定义teacherPinCheckinAPI和teacherUnpinCheckinAPI(已实现)
- 在
-
用户权限获取:
- 创建
useAuthRole.jscomposable - 实现实时请求用户角色接口(不依赖 localStorage)
- 返回
{ is_teacher: boolean }
- 创建
-
改造 CheckinCard:
- 引入
van-action-sheet - 在菜单中添加"置顶"/"取消置顶"选项(根据
is_pinned和is_teacher动态显示) - 实现置顶确认弹窗
- 引入
-
置顶逻辑实现:
- 对接置顶/取消置顶 API
- 成功后更新列表数据(本地更新,避免全列表刷新)
- 已置顶卡片显示 📌 图标和"置顶"标签
-
测试:
- 教师账号验证置顶功能
- 学生账号验证置顶菜单不显示
- 刷新页面验证置顶状态持久化
🔴 第 2 阶段: 评论功能 (高优先级)
目标: 实现用户发表评论功能
步骤:
-
环境准备:
- 在
.env添加VITE_FEATURE_CHECKIN_COMMENT=1 - 在
src/api/checkin.js中定义addCheckinCommentAPI
- 在
-
创建评论输入弹窗:
- 创建
CheckinCommentDialog.vue组件 - 实现
van-field文本输入框(多行模式) - 实现字符计数提示(最多 200 字)
- 创建
-
实现表情选择器:
- 创建
EmojiPicker.vue组件 - 使用
van-popup+van-grid展示表情符号 - 实现点击表情插入到输入框光标位置
- 定义常用表情列表(原生 Unicode Emoji)
- 创建
-
输入验证:
- 实现字符计数逻辑(
Array.from(text).length) - 纯文本评论至少 5 字
- 允许纯表情评论
- 实时提示剩余字数
- 实现字符计数逻辑(
-
对接 API:
- 实现发表评论接口调用
- 成功后 Toast "评论成功"
- 关闭弹窗并刷新评论列表
-
集成到 CheckinCard:
- 在
/checkin/index页面的打卡卡片菜单中添加"评论"选项 - 点击评论菜单打开
CheckinCommentDialog - 仅用户端可见(教师端不显示)
- 在
-
测试:
- 测试纯文本评论(最少 5 字)
- 测试纯表情评论
- 测试混合评论(文本 + 表情)
- 测试字符计数准确性
- 测试 200 字符限制
- 测试表情插入到光标位置
🔴 第 3 阶段: 评论列表 (高优先级)
目标: 实现评论列表展示、回复、删除功能
步骤:
-
环境准备:
- 在
.env添加VITE_FEATURE_CHECKIN_COMMENT_LIST=1 - 在
src/api/checkin.js中定义评论相关 API:-
getCheckinCommentsAPI- 获取评论列表 -
replyCheckinCommentAPI- 回复评论 -
deleteCheckinCommentAPI- 删除评论
-
- 在
-
创建评论列表组件:
- 创建
CheckinCommentList.vue组件 - 参考
StudyCommentsSection.vue的实现模式 - 实现首屏展示最多 5 条评论
- 超过 5 条显示"查看全部"按钮
- 创建
-
实现评论样式:
- 一级评论:
用户名: 评论内容 - 回复评论:
用户名 回复 被回复用户名: 评论内容 - 灰色背景,参考微信朋友圈样式
- Emoji 符号直接渲染(无需特殊处理)
- 一级评论:
-
实现回复功能:
- 点击评论弹出底部输入框(类似微信朋友圈)
- 输入框支持表情选择器(复用
EmojiPicker.vue) - 实现回复 API 调用
- 成功后刷新评论列表
-
实现删除功能:
- 仅显示自己评论的删除图标(根据
is_my字段判断) - 点击删除图标弹出确认框
- 确认后调用删除 API
- 成功后从列表中移除该评论
- 仅显示自己评论的删除图标(根据
-
实现懒加载:
- 首屏仅加载前 5 条评论(从
post.comments字段读取) - 点击"查看全部"调用
getCheckinCommentsAPI加载完整列表 - 实现分页加载
- 首屏仅加载前 5 条评论(从
-
集成到 CheckinCard:
- 在
CheckinCard底部引入CheckinCommentList - 根据
comments_count和comments字段渲染 - 评论发表成功后自动刷新列表
- 在
-
教师评论管理 (新增):
- 在
/teacher路由下的打卡列表中启用评论管理 - 教师可在评论列表中查看所有评论
- 在评论卡片右侧显示审核按钮(仅教师可见)
- 实现评论屏蔽/通过功能
- 对接
teacherReviewCommentAPI(新增) - 操作后实时更新评论状态
- 在
-
测试:
- 测试评论列表展示(一级评论、回复评论)
- 测试"查看全部"懒加载
- 测试评论回复功能
- 测试评论删除功能(自己的评论、别人的评论)
- 测试 Emoji 渲染
- 测试教师评论管理功能(屏蔽/通过)
- 测试权限控制(学生不可见审核按钮)
🟡 第 4 阶段: 海报功能 (中优先级)
目标: 实现打卡海报生成与分享
步骤:
-
环境准备:
- 在
.env添加VITE_FEATURE_CHECKIN_POSTER=1 - 安装依赖:
pnpm add html2canvas
- 在
-
创建海报组件:
- 创建
CheckinPoster.vue组件 - 参考
SharePoster.vue的实现模式 - 实现布局: 用户信息、打卡内容、二维码
- 创建
-
实现长图渲染:
- 支持长内容滚动查看
- 处理多图展示(网格布局)
- 处理文本换行
-
实现图片生成:
- 使用
html2canvas将 DOM 转为图片 - 配置
useCORS: true处理跨域图片 - 添加
crossorigin="anonymous"到图片标签 - 生成后展示图片并提示"长按保存"
- 使用
-
实现降级方案:
- 监听
html2canvas失败情况 - 失败时显示文字版卡片
- 提示用户"长按截图保存"
- 监听
-
集成到 CheckinCard:
- 在打卡菜单中添加"海报"选项
- 点击后打开
CheckinPoster弹窗 - 传入打卡信息
-
测试:
- 测试不同内容长度的海报生成
- 测试跨域图片处理
- 测试降级方案(生成失败时)
- 测试在微信内置浏览器的兼容性
- 测试长按保存功能
边界条件与注意点
1. 字符计数与 Emoji 处理
问题: Emoji 符号在某些设备上占用 2 个字符位置(代理对)
解决方案:
// 错误方式
text.length // 可能将 Emoji 算作 2 个字符
// 正确方式
Array.from(text).length // 正确计算字符数
验证规则:
- 纯文本评论至少 5 个字符
- 允许纯表情评论(无文字)
- 单条评论最多 200 个字符(包含 Emoji)
2. 数据库字符集支持
要求: 后端数据库需使用 UTF-8 MB4 字符集
- MySQL 5.5.3+ 的
utf8mb4 - 否则部分 Emoji(如 🎁)会乱码
3. 列表更新策略
原则: 避免全列表刷新,采用本地数据更新
实现方式:
- 置顶成功: 更新本地卡片的
is_pinned字段 - 评论成功: 在本地评论列表中新增评论项
- 删除成功: 从本地评论列表中移除评论项
- 仅在必要时调用接口刷新列表
4. 权限控制
置顶功能:
- 仅教师可见(通过
is_teacher字段判断) - 权限实时获取(不依赖 localStorage)
评论功能:
- 仅用户端可见(仅在
/checkin/index显示) - 允许自评
删除评论:
- 仅可删除自己的评论(通过
is_my字段判断) - 教师不可删除学生的评论
教师评论管理:
- 仅教师可见审核按钮
- 可屏蔽或通过任何评论
5. 性能优化
评论列表懒加载:
- 首屏仅加载前 5 条评论
- 点击"查看全部"时加载完整列表
- 实现分页加载
图片懒加载:
- 海报生成时使用
loading="lazy" - 避免一次性加载所有图片
列表虚拟滚动:
- 如果打卡列表超过 50 条,使用
van-list的虚拟滚动模式
6. 错误处理
网络错误:
- API 调用失败时,显示 Toast 提示用户
- 提供"重试"按钮
并发冲突:
- 删除评论时,若该评论已被删除,提示"该评论不存在"
- 评论发表时,若打卡已被删除,提示"该打卡不存在"
海报生成失败:
- 捕获
html2canvas异常 - 显示降级方案(文字版卡片)
7. 测试计划
单元测试:
- 测试
CheckinCommentDialog组件的输入验证 - 测试 Emoji 插入逻辑
- 测试字符计数准确性
集成测试:
- 测试评论发表、回复、删除的完整流程
- 测试置顶、取消置顶的完整流程
- 测试海报生成流程
边界测试:
- 测试 200 字符限制
- 测试纯表情评论
- 测试空评论提交(应被阻止)
- 测试长文本评论
兼容性测试:
- iOS Safari
- Android Chrome
- 微信内置浏览器
- 测试 Emoji 显示兼容性
数据字段扩展总结
post 对象新增字段
{
id: 123,
user: { name: "张三", avatar: "..." },
content: "今天完成了100天打卡!",
images: [...],
videoList: [...],
audio: [...],
likes: 10,
is_liked: false,
is_my: true,
// 🆕 置顶功能字段
is_pinned: false, // 是否置顶
pinned_at: null, // 置顶时间
// 🆕 评论功能字段
comments_count: 0, // 评论总数
comments: [] // 评论列表(最多 5 条)
}
comment 对象数据结构
{
id: 1, // 评论 ID
checkin_id: 123, // 关联的打卡 ID
user_id: 456, // 评论者 ID
user_name: "张三", // 评论者昵称
content: "干得漂亮!🎉", // 评论内容
parent_id: 0, // 父评论 ID (0 表示一级评论)
reply_to_user_id: null, // 被回复用户 ID
reply_to_user_name: null, // 被回复用户昵称
is_my: false, // 是否是自己发表的评论
status: 1, // 评论状态: 0-待审核, 1-已通过, 2-已屏蔽
reviewed_at: null, // 审核时间
reviewed_by: null, // 审核人 ID
created_at: "2026-01-26 10:00:00"
}
user 对象新增字段
{
id: 123,
name: "张三",
avatar: "...",
// 🆕 角色字段
is_teacher: false // 是否是教师(需实时请求后端获取)
}
API 接口汇总
置顶功能
-
POST /api/teacher/pin-checkin- 教师置顶打卡 (参数:checkin_id) -
POST /api/teacher/unpin-checkin- 教师取消置顶打卡 (参数:checkin_id)
评论功能
-
GET /api/checkin/comments- 获取评论列表 (参数:checkin_id,page,page_size) -
POST /api/checkin/comment- 发表评论 (参数:checkin_id,content) -
POST /api/checkin/comment/reply- 回复评论 (参数:checkin_id,content,parent_id,reply_to_user_id) -
DELETE /api/checkin/comment/:id- 删除评论 (参数:comment_id) -
POST /api/teacher/review-comment- 教师审核评论 (参数:comment_id,action)
权限获取
-
GET /api/user/role- 获取用户角色信息 (返回:{ is_teacher: boolean })
开发优先级总结
🔴 高优先级 (第 1 批)
- 置顶功能 - 实现教师置顶打卡
- 评论功能 - 实现用户发表评论
- 评论列表 - 实现评论展示、回复、删除
- 教师评论管理 - 实现教师审核评论
🟡 中优先级 (第 2 批)
- 海报功能 - 实现打卡海报生成与分享
里程碑
第 1 里程碑: 置顶功能完成
- ✅ 教师可以置顶/取消置顶打卡
- ✅ 置顶打卡显示 📌 图标
- ✅ 权限控制正确(仅教师可见)
第 2 里程碑: 评论功能完成
- ✅ 用户可以发表评论(文本 + 表情)
- ✅ 表情选择器正常工作
- ✅ 输入验证正确(最少 5 字,最多 200 字)
- ✅ 评论成功后通知后端(后端发送通知)
第 3 里程碑: 评论列表完成
- ✅ 评论列表正常展示(一级评论 + 回复评论)
- ✅ 回复功能正常工作
- ✅ 删除功能正常工作(仅删除自己的评论)
- ✅ 懒加载正常工作(查看全部)
- ✅ 教师可以审核评论(屏蔽/通过)
第 4 里程碑: 海报功能完成
- ✅ 海报正常生成
- ✅ 支持长内容
- ✅ 降级方案正常工作(生成失败时)
下一步行动
- ✅ 与后端/产品确认"待确认事项"中的所有问题
- ✅ 确认开发优先级和里程碑节点
- ⏳ 开始第 1 阶段开发(置顶功能)
- ⏳ 同步准备 API Mock 数据,以便前端独立开发
- ⏳ 确认后端 API 开发进度,确保前后端同步
附录: 常用表情列表
😊😂❤️👍🎉✨🙏💪🔥💯😍👏🤝🌟🎁
😭😡🤔💭😴🎂🌈⭐🌙☀️🌺🌸🍀🎁🎈
🎵🎶📱💻⚽🏀🎾🎯🎨🎬📷🎤🎧📚✏️📝
💡🔔💬📧📞
总计: 64 个常用表情符号
变更记录
2026-01-27:
- ✅ 与后端/产品确认所有待确认事项
- ✅ 新增教师评论管理功能
- ✅ 更新开发优先级(置顶、评论、评论列表为高优先级,海报为中优先级)
- ✅ 完善数据字段定义
- ✅ 完善业务规则(字符限制、权限控制、通知机制)
- ✅ 按优先级重新组织开发步骤