duoaohui
|
b6fe8b227a
|
视频会议
|
2026-05-23 21:59:05 +08:00 |
|
duoaohui
|
50234db7a9
|
视频会议
|
2026-05-23 21:49:08 +08:00 |
|
duoaohui
|
fb7b0a8dde
|
视频会议
|
2026-05-23 21:45:47 +08:00 |
|
duoaohui
|
391508f1c8
|
视频会议
|
2026-05-23 21:42:49 +08:00 |
|
duoaohui
|
4cfc16b6e1
|
视频会议
|
2026-05-23 21:41:50 +08:00 |
|
duoaohui
|
94b05ff181
|
视频会议
|
2026-05-23 21:36:29 +08:00 |
|
duoaohui
|
9df92977fd
|
视频会议
|
2026-05-23 21:35:46 +08:00 |
|
duoaohui
|
c118ac43f5
|
视频会议
|
2026-05-23 21:29:29 +08:00 |
|
duoaohui
|
a8db973411
|
视频会议
|
2026-05-23 21:26:52 +08:00 |
|
duoaohui
|
ea7cd1edbd
|
视频会议
|
2026-05-23 21:23:41 +08:00 |
|
duoaohui
|
55f678bd40
|
视频会议
|
2026-05-23 21:12:51 +08:00 |
|
duoaohui
|
69efb8e498
|
视频会议
|
2026-05-23 20:43:39 +08:00 |
|
duoaohui
|
ea1fbde7a2
|
视频会议
|
2026-05-23 20:36:15 +08:00 |
|
duoaohui
|
7402e72c08
|
视频会议
|
2026-05-23 19:54:21 +08:00 |
|
duoaohui
|
d45d6d9d4e
|
视频会议
|
2026-05-23 19:44:00 +08:00 |
|
duoaohui
|
2568f15bda
|
视频会议
|
2026-05-23 18:55:47 +08:00 |
|
duoaohui
|
39594388e3
|
视频会议
|
2026-05-23 17:57:26 +08:00 |
|
duoaohui
|
3e56a6c3db
|
视频会议
|
2026-05-23 17:51:35 +08:00 |
|
duoaohui
|
759ddb513d
|
视频会议
|
2026-05-23 17:15:19 +08:00 |
|
duoaohui
|
63c799c921
|
视频会议
|
2026-05-23 15:26:40 +08:00 |
|
duoaohui
|
3772b763c1
|
视频会议
|
2026-05-23 15:18:57 +08:00 |
|
duoaohui
|
2e66717932
|
视频会议
|
2026-05-23 14:15:19 +08:00 |
|
duoaohui
|
2689301721
|
视频会议
|
2026-05-23 12:53:30 +08:00 |
|
duoaohui
|
3e6ddb0e02
|
视频会议
|
2026-05-23 12:19:10 +08:00 |
|
duoaohui
|
6347fdd2db
|
视频会议
|
2026-05-23 09:35:03 +08:00 |
|
duoaohui
|
4c9f462ff1
|
视频会议
|
2026-05-22 11:16:10 +08:00 |
|
duoaohui
|
23a74f737e
|
视频保存
|
2026-05-19 17:21:40 +08:00 |
|
duoaohui
|
b8c2bf2dc4
|
视频会议
|
2026-05-18 23:13:59 +08:00 |
|
duoaohui
|
ac8ec4c903
|
视频会议保存
|
2026-05-18 19:42:55 +08:00 |
|
|
|
315884685f
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 21:15:39 +08:00 |
|
|
|
45c0852a36
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 21:12:21 +08:00 |
|
|
|
fce4a49971
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 19:59:28 +08:00 |
|
|
|
6c0491bf7d
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 19:38:19 +08:00 |
|
|
|
ec56cc4dd1
|
更新 frontend/src/utils/mediasoup-client.js
|
2026-05-16 19:11:35 +08:00 |
|
|
|
8292bf86e4
|
更新 frontend/vite.config.js
|
2026-05-14 20:40:04 +08:00 |
|
|
|
3b8cb6c274
|
更新 frontend/vite.config.js
|
2026-05-14 19:07:22 +08:00 |
|
bujinyuan
|
c35097a0d8
|
fix(meeting): Task 16 修复 code-reviewer 审计 P2 七项 + Nit 七项
覆盖 Phase 2e-2 代码审查报告(docs/reviews/2026-04-23-phase2e-2-code-review.md)
P2/Nit 收尾批次,均在本仓库完成闭环;剩余 5 项登记推迟至 Phase 2f/3。
===== P2 七项 =====
- P2-1 cleanupUserResources 补 transport 清理
· media-server 新增 DELETE /internal/v1/transports/:id + transport.service.closeTransport
· Go MediaOrchestrator 接口新增 CloseTransport;HTTP 实现按 doCloseRequest 走 4xx 幂等 + 指数退避
· meeting_signal_service.cleanupUserResources 新增 "transport:<id>" 分支
- P2-2 preview.vue 快速切设备竞态
· previewSeq 序号 + nextTick 后 stale 判断,丢弃过期结果
· onVideoChange/onAudioChange 走 scheduleRestartPreview 200ms 防抖
· onBeforeUnmount 清理 changeDebounceTimer
- P2-3 room.vue onLoad redirectTo 后补 return
· 引入 redirectingToJoin 守卫,跳转页不再执行 onMounted 初始化
- P2-6 generateUniqueRoomCode 重试上限监控
· 重试后成功:Warn 日志(码空间健康度告警)
· 重试耗尽:Error 日志 + ErrRoomCodeConflict
· 修正 logs.Error 调用签名(去掉多余的 nil)
- P2-7 SendChatMessage 服务端长度 + 频率限制
· 新增 ErrChatContentEmpty / ErrChatContentTooLong / ErrChatRateLimited
· utf8.RuneCountInString 校验 500 字符上限
· Redis INCR + EXPIRE 滑动窗口(30 条/60s,首次写入 EXPIRE 兜底)
· controller.handleError 映射为 HTTP 400
- P2-8 MEETING_ENDED_REASON_LABEL 覆盖复核
· 新增前端专属常量 MEETING_ENDED_REASON_KICKED + 文案
· store/meeting.js _onMemberKicked 使用常量
· 同步修复后端 OnWSDisconnect 硬编码 "ws_disconnect" → constants.MeetingLeftReasonDisconnect
- P2 已修 P2-1/2/3/6/7/8;P2-4(WS token 迁出 URL query)与 P2-5(Chat 服务拆分)登记推迟
===== Nit 七项 =====
- Nit: kind:id 解析改用 strings.SplitN
· cleanupUserResources / pushExistingRoomState 两处同步
- Nit: resourceTTL 中央化
· 新增 constants.MeetingResourceTrackTTLSeconds(3600)
· meeting_signal_service.go resourceTTL 由 const 改 var 并引用常量
- Nit: ws/handler.go CheckOrigin 白名单
· NewHandler 新增 serverCfg 依赖;checkOrigin 支持同源放行 / dev 模式放行 / release 模式白名单严格匹配
· config.ServerConfig 新增 WSAllowedOrigins(逗号分隔)+ AllowedOrigins() / IsRelease() 辅助方法
· provider.go 新增 provideServerConfig,wire_gen.go 同步
- Nit: http_media_orchestrator.go 超时 + CreateRouter 重试
· 默认 TimeoutMS 5000→10000ms 兼容 Worker 冷启动
· 新增 CreateRouterRetry(默认 1 次,300ms 退避),仅对非 404 错误重试
· config.dev.yaml / config.docker.yaml 同步写入显式配置
- Nit: deploy-public.sh REDIS_PASSWORD × redis.conf 联动校验
· 检测 REDIS_PASSWORD 与 redis.conf 的 requirepass 配对一致性
· redis.conf 增加公网部署 requirepass 使用说明
- Nit: media-server internal-auth isPrivatePath 按 path 匹配
· 剔除 query/hash 后再与白名单 startsWith,避免 "?" 语义混淆
- Nit: mediasoup-client.js in-flight 锁走读确认
· finally 分支已覆盖 resolve/reject 两路,追加注释强化语义
- Nit 走读复核:_onMemberLeft 整槽关闭 vs _onProducerNew(closed=true) 精确匹配 producerId
· 粒度正确,无需改动(登记结论)
===== 构建验证 =====
- go vet ./... / go build ./... 通过
- frontend npm run build:h5 通过(仅 uni-app legacy warning,无 error)
- media-server npx tsc --noEmit 通过
===== 审查追踪小节 =====
docs/reviews/2026-04-23-phase2e-2-code-review.md 追加 "Task 16 修复追踪(2026-04-24 更新)":
- 已修复一览(本批次 14 处 + 历次 commit cdaa39d / ea2bf96 / f5ae095 / 5ed14c2)
- 推迟登记表(P2-4 / P2-5 / 端口收敛 / appData 校验 / RFC3339 时间格式,共 5 项)
Made-with: Cursor
|
2026-04-23 17:45:03 +08:00 |
|
bujinyuan
|
f5ae095033
|
fix(meeting): Task 16 会议销毁路径资源清理专项
前端修复:
- _onRoomEnded 保留结束页数据,新增 exitEndedRoom() 动作
room.vue onUnload 在 ENDED 状态调用,释放 currentRoom / participants / chatMessages 等 pinia state
- endMeeting() API 成功后立即触发 _onRoomEnded 本地兜底,不再依赖 room.ended 广播
避免 API 成功但 WS 抖动导致 engine / timer / AudioContext 泄漏
- 新增 _pendingBroadcastTimers 集合收纳 _broadcastSelfState 的 setTimeout 句柄
_cleanupMedia 统一 clearTimeout,避免会议结束后 stale 重试逻辑排队
后端修复:
- MeetingLifecycleService.OnRoomEnded 统一生命周期收尾
取消 graceTimers / emptyTTLTimers + Pipeline DEL host_grace / empty_ttl / 两个 handling 锁 key
- EndRoom 事务提交后遍历 activesBefore 调 cleanupRoomRedisResidual
批量 DEL 每个前任活跃成员的 resourceTrackKey + memberStateKey
补调 lifecycleSvc.OnRoomEnded 清理自身 timer + keys
- HandleEmptyRoomExpired MarkEnded 后补调 cleanupRoomRedisResidual + 清 host_grace 残留
验证:go vet + go build + npm run build:h5 全部通过
Made-with: Cursor
|
2026-04-23 17:12:38 +08:00 |
|
bujinyuan
|
ea2bf96c2f
|
fix(meeting): Task 16 修复 code-reviewer 审计 P1 八项
按 docs/reviews/2026-04-23-phase2e-2-code-review.md 清单落地 P1 批次:
- P1-1 主持人转让原子性:MeetingParticipantDAO.TransferHost 内部事务
合并 meeting_rooms.host_id 更新,service/lifecycle 移除冗余 UpdateHost
- P1-2 ListChatMessages / ListMyMeetings 回归 DAO:新增
MeetingChatDAO.ListByRoomBefore 反向游标,service 移除 s.db 直查
- P1-3 ListMyMeetings N+1 放大优化:新增
MeetingParticipantDAO.ListJoinedRoomsByUser JOIN + DISTINCT 单次 SQL
- P1-4 EndRoom 行锁 + 事务:s.db.Transaction 包裹
SELECT FOR UPDATE → 快照 → MarkEnded → LeaveAllActive;DAO 补
WithTx(*gorm.DB) 和 GetByIDForUpdate
- P1-5 后台 goroutine trace_id 保留:新增 logs.DetachContext(ctx);
替换 meeting_service / meeting_signal_service 共 10 处 context.Background();
meeting_ws_handler 每条 WS 消息分配独立 trace_id
- P1-6 _broadcastSelfState 重试:最多 2 次指数退避(700ms→2100ms),
检测 localAudioEnabled/localVideoEnabled 已被后续动作覆盖时放弃旧 patch
- P1-7 HandleHostGraceExpired / HandleEmptyRoomExpired 处理锁:
独立 host_grace_handling:<code> / empty_ttl_handling:<code> SETNX + 60s TTL,
消除 Redis 自然过期 + 本地 timer 到点 + DEL 返回 0 的盲区
- P1-8 pushExistingRoomState 同步化:OnRoomJoin 返回前完成补推,
消除与 REST member.joined 并行造成的前端状态闪烁
并附带修复 .gitignore 中 logs/ 规则误伤 pkg/logs/ 代码目录的历史遗留问题,
将 logger.go / trace.go 正式纳入版本控制。
验证:backend go build + go vet 通过;frontend npm run build:h5 通过。
Made-with: Cursor
|
2026-04-23 16:53:30 +08:00 |
|
bujinyuan
|
cdaa39d686
|
fix(meeting): Task 16 修复 code-reviewer 审计 P0 四项 + 落盘审查报告
code-reviewer 子代理(docs/reviews/2026-04-23-phase2e-2-code-review.md)对 Phase 2e-2
整体做全栈审计,暴露 P0 × 4 / P1 × 8 / P2 × 8 / Nit × 11。本次先修 P0:
- P0-1 / P0-2 媒体资源归属越权:
新增 assertOwnsResource(roomCode, userID, kind, id) helper,用 resourceTrackKey Redis Set
SIsMember 校验 transport/producer/consumer 归属;统一接入 OnTransportConnect /
OnProduceStart / OnConsumeStart / OnConsumeResume / OnProducerClose 五个信令入口,
一次封死同类横向越权漏洞(Redis 查询失败时 fail-closed,避免抖动开口子)。
新增 service.ErrResourceNotOwned 错误常量。
- P0-3 CreateRoom Router 失败吞错:
旧实现 mediaErr != nil 仅 logs.Warn 继续返回成功 → zombie 房间 + 入会必败 +
一人一会议名额被占。改为 fail-closed 补偿:LeaveRoom + MarkEnded(system_error) +
返回新 service.ErrMediaServiceUnavailable,controller.handleError 映射为 500。
- P0-4 会议密码明文走 URL:
join.vue onNext 旧逻辑把密码拼 ?password=xxx,浏览器历史 / DevTools 留痕。
新增 meetingStore.draftJoinPayload 内存态草稿,join.vue 只跳转
?mode=join&code=xxx,preview.vue onLoad 读取后立即 draftJoinPayload = null 清空,
邀请链接里的 password 参数一律忽略。
验证:
- cd backend/go-service && go build ./... 通过
- cd frontend && npm run build:h5 通过
- 仅剩 1 个预先存在 unusedparams warning,与本次改动无关
P1 / P2 / Nit 清单保留在审查报告,按顺序继续处理。
Made-with: Cursor
|
2026-04-23 16:34:13 +08:00 |
|
bujinyuan
|
790996feaf
|
feat(meeting): Task 15 UI 打磨 + 主持人四件套 + 媒体层稳定性回归修复
- Task 15 UI:6 项原创特色(说话者流光 / 柔性网格 / 自视频浮窗 / 静音氛围色 / 入会滑入 / NetworkBadge 3 条波浪)+ 说话者双源探测(RTP audioLevel + WebAudio RMS)+ 主持人四件套(静音/开麦/转让/踢出)
- 新增 SelfVideoFloat 浮窗组件 + 4 份 design-system 页面文档(home / preview / room / invite)
- 媒体回归补丁(手工联调触发):
* 后端 signal_service 在 OnRoomJoin 追加 pushExistingRoomState → 向新加入者补发历史 producers + 历史成员 audio/video 状态
* 新增 Redis Hash memberStateKey 持久化成员 audio/video enabled,OnMemberStateChanged 落盘、cleanupUserResources 清理
* 前端 _broadcastSelfState 在本地音视频开关末尾同步 state.changed;_afterJoined 先 getRoom 再 room.join 修复后入者成员状态时序;_onMemberStateChanged 占位兜底
* _cleanupRemoteProducer 改为按 producerId 精准清理(修复 "关音频误关视频")
* _onRoomEnded + createAndEnter/joinAndEnter 强化 reset(修复重入会看不到自己画面)
* mediasoup-client ensureSend/RecvTransport 引入 in-flight Promise 锁防并发重复建连
- 文档同步:CURRENT_STATUS.md / Task 15 plan / phase2e-2 implementation plan
Made-with: Cursor
|
2026-04-23 16:04:46 +08:00 |
|
bujinyuan
|
d6fb7ce291
|
feat(meeting): Task 13 完成 meeting_invite 通知卡片对接
## 后端
- MeetingService.InviteUsers 的 PushPayload.Extra 补齐设计 §10.1 要求字段:
inviter_id / inviter_name / inviter_avatar / expired_at(Unix 秒,
与 Redis TTL MeetingInviteTokenTTL=600s 对齐)
## 前端
- constants/notify.js:supportsInlineAction 扩展支持 meeting_invite;
新增 NOTIFY_INLINE_ACTION_LABEL 映射(meeting_invite → 立即加入/稍后)
+ NOTIFY_INLINE_ACTION_DEFAULT 兜底
- components/notify/NotifyItem.vue:
* 按 type 动态渲染按钮文案(actionLabel 计算属性)
* isExpired 计算属性:meeting_invite 比对 extra.expired_at * 1000
* 过期态合并为单个 disabled 的"邀请已过期"按钮
* 新增 .notify-btn--expired 灰显样式
- pages/notify/index.vue:
* handleAccept / handleReject / _navigateByNotify 增加 meeting_invite 分支
* 新增 _navigateToMeetingInvite 辅助:跳 /pages/meeting/preview?mode=join&code=xxx
* 过期时 toast "邀请已过期"不跳转,"稍后"仅 markRead 不发接口
## 文档
- docs/plans/2026-04-21-phase2e-2-implementation.plan.md:Task 13 标 ✅ + 详细产出
- docs/progress/CURRENT_STATUS.md:新增 Task 13 专节(关键技术点 / 验证记录 / 下一步)
- .cursor/rules/project-context.mdc:2e-2 进度刷 Task 0-13 ✅
## 验证
后端 curl 双角色端到端(testuser1 创会 → 邀请 testuser2 → 拉通知列表):
- type=meeting_invite,extra 8 字段齐全
- expired_at = invited_at + 600,与 Redis TTL 对齐
Made-with: Cursor
|
2026-04-22 17:26:31 +08:00 |
|
bujinyuan
|
310ea03ce1
|
fix(im): 修复发送方消息一直卡在"发送中"圆圈、下方不显示已读/未读的 Bug
现象
----
在单聊里发送新消息后,本人气泡左侧一直显示 loop 圆圈(_sending=true),
气泡下方看不到"已读 / 未读"标签;但对端能正常收到消息。刷新页面、
走 fetchHistory 之后才能恢复正常显示。
根因
----
1. services/websocket.js 的 _onMessage:一旦发现 event 以 ".ack" 结尾,
就走 _handleAck(seq→pendingAcks) 处理完后 **直接 return**,不再 _emit。
这样通过 on('im.message.send.ack') / on('im.message.read.ack')
订阅 ACK 的 listener(chat store 的 _onSendACK / _onReadACK)
**永远收不到回调**,导致 _sending 标记一直没被清、id 没被合入,
模板里 getReadLabel() 因为 !msg.id / _sending=true 返回空字符串。
2. store/chat.js 的 _appendMessage:按 client_msg_id 命中旧临时消息时,
只是 isDup=true 直接 return,把服务端权威字段(包括 id)丢弃了,
没有形成有效的兜底路径。
修复
----
- websocket.js: .ack 报文在 _handleAck 之后 **继续走 _emit**,让 Promise
通路(sendWithAck → pendingAcks)与订阅通路(on(...ack))并行分发,
互不冲突。这是真正解决"卡圈"的主修复。
- chat.js _appendMessage: 把 dedup 改成 "就地合并" 语义:
* 同 id 命中 → 真重复丢弃(不回退)
* 同 client_msg_id 命中本地 _sending 临时消息 → 用服务端字段
覆盖 id/created_at/..., 同时清 _sending/_failed
* 其余情况才 push 新条
作为广播帧先于 ACK 到达时的二级兜底,也保证后续再有 ACK/广播
重复到达不会产生重复气泡。
影响面
------
- 单聊/群聊 发送消息后"已读 / 未读 / N人已读"标签恢复实时显示
- im.message.read.ack(对方读到某条)也能正常驱动 readStatusMap 更新
- meeting 等模块的 sendWithAck() 不受影响(走的是 pendingAcks Promise
通路,本次只是在此之后多了一个 _emit 分发,对业务无副作用)
Made-with: Cursor
|
2026-04-22 17:05:31 +08:00 |
|
bujinyuan
|
cacf4ae074
|
fix(meeting): 提升 toolbar z-index 确保成员/邀请面板开启时仍可点击
MemberPanel 和 InviteDialog 的 .panel-root 使用了 position:fixed; inset:0;
z-index:200 的全屏遮罩,当面板打开时会覆盖整个视口,包括底部 toolbar。
这导致:
- 用户再次点击 toolbar 的"成员"/"邀请"按钮时,命中的是 mask 而非按钮
- mask 的 @click.self 虽然也会关闭面板(通过点击空白区域),
但与 toolbar 按钮 toggle 的用户预期行为不一致
修复:把 MeetingToolbar .toolbar 的 z-index 从 15 提升到 210,
确保其高于 .panel-root (z:200) 而低于 .leave-mask (z:220)。
验证:Playwright 实测点击成员按钮 hitSelf=true,打开后再次点击正常关闭。
Made-with: Cursor
|
2026-04-22 16:39:41 +08:00 |
|
bujinyuan
|
a311c03a21
|
fix(meeting): 聊天面板点击失效 & 成员/邀请按钮支持 toggle
ChatPanel 的 .btn-send 使用了 all:unset 导致 position 变为 static,
其 uni-app 自动注入的 ::after 伪元素(inset: 0 -1200px -80px 0)
absolute 定位会回溯到 body 作为 containing block,
形成一个覆盖几乎整个视口的隐形遮罩层,导致聊天面板的关闭按钮 (.btn-close)、
输入框 (textarea) 甚至发送按钮本身周围区域都无法点击/输入。
修复:
1. ChatPanel 的 .btn-send::after 从仅 border:none 升级为
content:none + display:none,彻底抹除遮罩
2. room.vue 中 openMembers / openInvite 改为 toggle 切换,
解决"再次点击成员按钮面板不会关闭"的问题
验证:Playwright 真实 DOM 命中测试 close/textarea/send 全部 hitSelf=true,
成员按钮二次点击面板正常关闭。
Made-with: Cursor
|
2026-04-22 16:34:30 +08:00 |
|
bujinyuan
|
3aa8174bc0
|
fix(meeting): 抹除 uni-button ::after 遮罩修复工具栏点击失效
uni-app H5 会给每个 <button> 编译后的 <uni-button> 自动注入一个
::after 伪元素作为点击态反馈遮罩,其 inset 为 0 -1200px -80px 0
(即 2400px × 160px),向按钮右侧扩展 1200px,下方扩展 80px。
这导致会议工具栏(MeetingToolbar)里每个按钮的 ::after 都横跨整个
toolbar,最后一个"离开"按钮 DOM 顺序最上,其 ::after 覆盖了所有
兄弟按钮的命中区域:点击"静音/视频/邀请/成员/聊天"时实际都命中
"离开"按钮的 ::after,触发离开弹窗;且弹窗中的"取消"按钮同样被
右侧"结束会议"按钮的 ::after 遮挡,点击取消反而会触发结束会议。
修复:在 .btn 和 .leave-btn 上显式设置 ::after { content: none;
display: none },并把按钮设为 position: relative 固化堆叠上下文。
验证:Playwright 真实点击各按钮 hitSelf=true,取消按钮不再
错误触发结束会议逻辑。
Made-with: Cursor
|
2026-04-22 16:27:16 +08:00 |
|
bujinyuan
|
ff46dc5f76
|
fix(phase2e-2): 视频 HD 分辨率 + toolbar 防溢出 + 聊天按钮 toggle
问题
1. 进入会议后视频比预览页更"近、更糊"
· 预览 / startLocalVideo 均固定 640x480,mediasoup 视频轨清晰度偏低
· 进房后 videoTile cover 裁切在大画布更明显 → 观感"放大"
2. 会议内聊天 / 邀请 / 成员按钮"点击无反应"
· 核心:.body flex 子内容过高时 toolbar 未固定收缩,被挤出视口
下方 (y > vh),用户点击位置在屏幕外
· 次要:openChat 仅 set true,已打开状态下再次点击无视觉反馈
3. 聊天按钮无法关闭面板:只有面板内 × 按钮能关
修复
- store/meeting.js startLocalVideo + preview.vue getUserMedia 统一
改为 ideal 1280x720 + frameRate ideal 24 / max 30,浏览器在设备不
支持时自动降级;进房与预览视觉表现一致
- components/meeting/MeetingToolbar.vue .toolbar 加 flex-shrink:0
+ position:relative + z-index:15,保证 toolbar 永远贴底不被 body
压缩,且点击层级高于 ChatPanel 背景
- pages/meeting/room.vue .top-bar 加 flex-shrink:0,避免 body 溢出
时顶栏压缩引发整体错位
- pages/meeting/room.vue openChat 改 toggle:已打开则关闭,未打开则
展开并 markChatsRead,交互更符合直觉
验证(Playwright 1024x576)
- 预览 getVideoTracks()[0].getSettings() → 1280x720 / 24fps ✓
- 进房后 localVideo settings 同样 1280x720 ✓
- 聊天按钮 boundingRect y=505 bottom=567 ≤ vh 576,inViewport=true ✓
- 点击聊天 → chat-col.open=true 宽度 320.5px;再点击 → open=false ✓
- 截图 room-hd-chat-open.png:视频清晰、聊天面板展开、toolbar 贴底
Made-with: Cursor
|
2026-04-22 16:13:25 +08:00 |
|
bujinyuan
|
ce09315fa6
|
feat(phase2e-2): 会议首页落地 "创建 / 加入" 双入口
问题
- tabBar "会议" 指向 pages/meeting/index,但该页仍是占位的
"视频会议功能开发中…",用户从底部 tab 进入无入口按钮,
必须手改 URL 到 /pages/meeting/create 或 /join 才能进会议
修复
- 重写 pages/meeting/index.vue 为真实的 Meeting Hub:
· 顶部 hero 标题 + 描述
· "创建即时会议" 主按钮(蓝色渐变卡片,跳 /pages/meeting/create)
· "加入会议" 次按钮(卡片,跳 /pages/meeting/join)
· 提示文案说明入会流程与主持人能力
- 保留 CustomTabBar 底栏 current=2
验证
- Playwright 截图确认布局、两个入口按钮视觉与点击区域均正常
Made-with: Cursor
|
2026-04-22 16:04:06 +08:00 |
|
bujinyuan
|
c529ff4689
|
fix(phase2e-2): 全局 H5 容器铺满视口 + 静默自动清理 stale 会议
问题
1. 桌面浏览器大窗口下所有页面内容偏左上、右下大片空白:
uni-app H5 的 html/body/#app/uni-page-* 默认不显式 100% 高度,
且 .room 仅靠 position:fixed inset:0 在某些模式下可能被祖先
容器尺寸限制,导致 toolbar / tabBar 不贴底、内容挤在左上角
2. 刷新会议后再次加入会触发 toast "你不在当前会议中":
cleanupStaleMeetings 对列表里已过期的会议调 leave 必然返回 400,
被全局 request 拦截器误报给用户
3. 会议界面部分按钮点击无反应:
根因同 #1,布局错位后按钮视觉位置与 hit box 不一致
修复
- App.vue: H5 条件编译注入全局基础样式,强制 html/body/#app/uni-app/
uni-page/uni-page-wrapper/uni-page-body 宽高铺满,min-height 100%
- room.vue: .room 追加 width/height/min-width/min-height: 100vw/100vh 冗余
+ z-index:10,彻底兜底 position:fixed 失效场景
- request.js: 新增 options.silent 选项,业务错误 / 401 / 403 / 500 /
网络异常均根据 silent 决定是否弹 toast;401 仍会跳登录
- api/meeting.js: createRoom / joinRoom / leaveRoom 均接受 options 参数
- store/meeting.js:
· cleanupStaleMeetings 内 leave 传 silent:true 忽略幂等错误
· joinAndEnter / createAndEnter 首次尝试(未重试时)传 silent:true,
若命中 staleMeetingHint 自动清理重试,对用户完全无感;重试仍失败
的真实错误走正常 toast
验证
- Playwright 1024x576:room 页 / profile 页 / chat 列表 / 刷新后重新加入
流程截图均显示全屏铺满、tabBar/toolbar 贴底、无误报 toast
- 刷新 → join → 预览 → 加入 日志:首次 400 静默 → 自动清理 → 重试加入
成功,MediaEngine 正常建立
Made-with: Cursor
|
2026-04-22 16:00:12 +08:00 |
|
bujinyuan
|
0c9f0483e5
|
fix(phase2e-2): 修复刷新后重入会失败 + 聊天输入区布局防御
问题
- 刷新会议页面被踢回 join 页,再次加入报"已在会议中"
- 极窄聊天面板下,发送按钮在 uni-app H5 渲染为 uni-button 时可能被默认
width:100% 样式撑满导致"竖排变形"
修复
1. meeting store: joinAndEnter / createAndEnter 捕获 staleMeetingHint 时自动
调用 cleanupStaleMeetings 清理后端残留的僵尸活跃成员记录,并重试一次;
日志链路:加入失败 → 检测僵尸 → 自动清理 → 重试加入
2. ChatPanel: input-area 显式声明 flex-direction:row + flex-wrap:nowrap;
.btn-send 追加 width:auto / line-height:1 / box-sizing,并通过 ::v-deep
复位 uni-app 内部 <uni-button> 默认 width,彻底避免按钮独占一行变形
验证
- Playwright 刷新 room 页 → 回 join → 预览 → 加入会议:日志显示自动清理僵尸
"581-702-753"、重试加入 "925-409-354" 成功,MediaEngine 正常建立
- 1024x576 视口下 toolbar 完整可见,聊天面板并排无右侧留白,发送按钮在
输入框右侧
Made-with: Cursor
|
2026-04-22 15:45:56 +08:00 |
|