Commit Graph

51 Commits (70e7a1c900556df3dfa101edbd4aa8a3cfa76376)

Author SHA1 Message Date
YunaiV 70e7a1c900 【新增】IM:群头像支持成员头像九宫格兜底
群头像为空时,取前 9 个成员头像在 Canvas 上拼九宫格 dataURL;空头像 / 加载失败的格子画跟 UserAvatar 同款色卡(首字 + charCode 哈希调色板)。
- 新增 GroupAvatar 组件包一层 UserAvatar;按容器 size × DPR 自适应画布像素,避免 retina 屏糊
- utils/group.ts 加 buildGroupAvatar 与 LRU 缓存 facade(上限 200);utils/image.ts 抽公共 loadImage;utils/user.ts 抽 getAvatarText / getAvatarBgColor 供 UserAvatar 与拼图共用
- GroupItem / GroupInfo / ConversationItem / ConversationPickerPanel 按会话类型分支换用 GroupAvatar
2026-05-08 18:27:53 +08:00
YunaiV c5b082ca80 ♻️ refactor(im): 业务策略数值从 ImCommonConstants 上移到 ImProperties(按 group / message 子模块分组),常量类仅保留 AT_USER_ID_ALL 协议契约值
♻️ refactor(im): 抽出 utils/config.ts 集中数值常量,按业务域统一前缀(GROUP_ / MESSAGE_ / FRIEND_ / CONVERSATION_ / FORWARD_),constants.ts 只留协议枚举与契约值
2026-05-08 17:42:13 +08:00
YunaiV 312df4c73d refactor(im): 抽象 IM 选择类弹窗为 PickerPanel 体系,对齐微信 PC
- 拆「业务壳 + 纯 PickerPanel」两层;13 个 dialog 统一 ref + open(opts) 接口
- 新增 FriendPickerPanel / ConversationPickerPanel / GroupMemberPickerPanel
- 抽 useFriendBuckets / useSelectedItems composable + buildDefaultGroupName / picker-dialog.scss mixin
- conversationStore 加 recentForwardConversationKeys 系列 action(持久化到 IDB)
- 三态语义固化:hide > locked > disabled
- 圆形勾选用微信绿;主按钮跟随项目主题色;最近转发横向头像 + 移除模式
- 删 GroupMemberSelector(由 GroupMemberPickerPanel 替代)/ FriendLite.deleted 死字段
- 配套:project_duibiao/im/dialog-picker-{contract,wechat-compare}.md
2026-05-08 14:06:48 +08:00
YunaiV 40ac2daca8 feat(im):文本气泡 @ 高亮支持点击 + URL 自动识别成可点击链接
- @ 段:群消息按 atUserIds 反查群成员,候选 name 兼容历史字面量(真实昵称 / 好友备注 / 群自定义昵称),displayName 统一收敛到 nickname,让历史消息也能渲染成 @真实昵称;@ 段点击弹 UserInfoCard
- @所有人:注入 IM_AT_ALL_USER_ID 虚拟候选,对齐微信 PC 仅高亮配色不挂点击
- 同名歧义:同字面量对应多个 userId 时标记 ambiguous,parser 整段消费成普通文本,避免错绑用户
- URL:识别 http(s) / www. 起头链接,<a target="_blank"> 新标签打开;默认补 https://
- TipSegment 加 link 变体作为统一文本片段类型,TEXT 气泡与灰条 tip 共用 TipSegments 组件渲染
- MessageInput @ token 文本统一用真实昵称,不再掺好友备注 / 群自定义昵称
2026-05-08 01:23:09 +08:00
YunaiV 094ab44094 🎨 refactor(im): joinMentionSegments 用 flatMap 替代命令式 push
少一个可变 out + 命令式 forEach,意图更直观

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 23:59:00 +08:00
YunaiV 9eb221e8d2 feat(im): 灰条 tip 文案的 mention 段支持点击弹 UserInfoCard
群广播 / 撤回 / 好友事件 tip 文案从纯字符串拆成 TipSegment[],mention
段携带 userId,渲染层挂点击 → uiStore.openUserInfoCardAtEvent。

- utils/message.ts:加 TipSegment 协议 + 零依赖 helper
- utils/user.ts、utils/conversation.ts:加 segments builder,string 版
  改写为 segmentsToText 包装,避免 case 表分叉
- TipSegments.vue:按 activeConversation 推断 addSource,群里走
  GROUP+群名、私聊走 SEARCH;nickname 不传备注避免 UserInfo 首屏闪
- MessageItem.vue / MessageHistory.vue:tip 块切 <TipSegments>

顺手补:utils/constants.ts 新增 SystemUserSexEnum,替换 IM 模块 sex
硬编码 1 / 2

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 23:46:50 +08:00
YunaiV cf01143632 feat(im): 初始化消息转发 v0.1:第一次评审 2026-05-07 19:47:27 +08:00
YunaiV d175fe86be feat(im): 初始化群名片 v0.2:第二次评审(需求各种进群的小问题) 2026-05-07 17:28:16 +08:00
YunaiV 65d5aacac9 feat(im): 初始化群名片 v0.1:第一次评审 2026-05-07 13:07:56 +08:00
YunaiV 0eca952c6a feat(im): 初始化表情包 v0.3:第四把 review(增加表情管理的界面) 2026-05-06 23:00:08 +08:00
YunaiV 8eebfd4744 feat(im): 初始化表情包 v0.1:第二把 review 2026-05-06 20:50:55 +08:00
YunaiV 1ed5dc7e6a feat(im): 初始化表情包 v0.0:第一把 review 2026-05-06 19:42:19 +08:00
YunaiV 8fc5273a88 feat(im): 初始化群申请 v0.1:第二把 review 2026-05-06 18:52:30 +08:00
YunaiV 3be0daf115 feat(im): 初始化群申请 v0.0:第一把 review 2026-05-06 14:53:47 +08:00
YunaiV 4868d69ed8 feat(im): 优化名片消息类型 v0.4:增加转发成功失败的提示 2026-05-06 12:18:31 +08:00
YunaiV 1ac0650984 ♻️ refactor(im):注释对齐 + patchMessage 复用 applyServerMessageUpdate
- recomputeConversationLast / videoCoverUrl / showSendingLoading 三处 JSDoc 跟实现对齐:
  原描述还停留在旧设计(lastSendTime 不重算 / 占位 coverUrl 用 blob / 「外层 loading 多余」),
  这轮一并改成当前事实,避免后续维护被误导
- patchMessage 删手写 revoke + Object.assign,改调 applyServerMessageUpdate,
  与 ackMessage / insertMessage(existingIndex) 共用一份服务端字段合并语义;
  「值未变早返回」保留在 patchMessage 顶部
- 抽 BLOB_URL_PREFIX 常量替代散落在 utils/message.ts 与 useMediaUploader.ts 的 3 处 'blob:' 字面量
2026-05-06 10:38:43 +08:00
YunaiV 59aab8ecdc feat(im): 优化名片消息类型 v0.3:增加表情选择 2026-05-06 08:47:18 +08:00
YunaiV b17f7a57e5 feat(im): 优化发送中的能力 v0.2:简化各种 kind、复用各种逻辑 2026-05-06 08:46:33 +08:00
YunaiV f3de29f95f feat(im): 增加名片消息类型 2026-05-06 08:00:36 +08:00
YunaiV a9f54fdee1 feat(im): 重构普通消息类型,和 openim 的消息编号对齐 2026-05-05 21:56:05 +08:00
YunaiV 390b66aee8 feat(im): 群禁言/封禁 UI 交互 + 群主解散群聊
一、群禁言交互
- MessageItem 右键菜单新增「禁言/解除禁言/移除」(权限校验)
- 新增 GroupMuteMemberDialog 禁言时长选择弹窗
- ConversationGroupSide 新增「全群禁言」开关
- MessageInput 新增禁言/封禁覆盖层,拦截所有发送入口
- canManageSender 目标角色未知时不展示管理菜单
- 全群禁言群主按 ownerUserId 直接豁免,不依赖成员列表

二、群封禁(GROUP_BANNED 1533)
- constants 激活 GROUP_BANNED 常量,扩展 isGroupNotification 范围
- groupStore 处理 1533 事件,实时更新 banned 字段
- MessageInput 覆盖层最高优先级判断 banned(红底)
- Group 类型、convertGroup 映射 banned 字段

三、禁言事件(1512-1515)
- constants 激活 GROUP_MEMBER_MUTED / CANCEL_MUTED / MUTED / CANCEL_MUTED
- groupStore 处理 1512-1515 事件,更新 muteEndTime / mutedAll
- user.ts 新增 4 条禁言 + 2 条封禁通知文案
- GroupNotificationPayload 扩展 mutedUserId / muteEndTime / banned

四、群主解散群聊
- ConversationGroupSide 底部按钮:群主显示「解散群聊」,非群主显示「退出群聊」
- 新增 handleDissolve 实现(二次确认 → dissolveGroup API → 清本地数据)
2026-05-05 18:41:28 +08:00
YunaiV 4d006f8e73 feat(im):将"免打扰"字段从 muted 全量重命名为 silent(DO/VO/Service/Mapper/测试/SQL + 前端 types/store/组件/管理后台),为后续 mute 禁言功能腾出词族 2026-05-05 13:51:53 +08:00
YunaiV 14e3f85cb0 ♻️ refactor(im):用户申请列表,增加流式查询,避免一次性加载过多,或者历史无法被加载到。 2026-05-04 22:46:20 +08:00
YunaiV b242b017c0 feat(im):fetchFriends 加 pending 去重 + FRIEND_APPLICATION 重命名 RECEIVED + inflight 命名调整为 pending 2026-05-04 19:14:51 +08:00
YunaiV 7618d58a66 feat(im):好友模块 code review 多项修复(补 block/unblock 全链路 + UserInfo 菜单入口、silent 后主动入库、防 currentUserId 切账号失活、雪崩去重与命名/枚举清理) 2026-05-04 17:31:21 +08:00
YunaiV 1b51926b19 refactor(im): 移除 TIP_TIME 消息类型,时间分隔条改为渲染时按 prevMessage.sendTime 计算
顺带修复 Bug-Y(删除最后一条消息后孤立时间分隔条)
2026-05-04 16:05:23 +08:00
YunaiV 89ee5d51ea feat(im): 增加好友申请的逻辑(v1.2:增加相关枚举、字典,减少硬编码) 2026-05-04 10:44:09 +08:00
YunaiV f86cd30af4 feat(im): 增加好友申请的逻辑(v1) 2026-05-04 09:18:35 +08:00
YunaiV 01e0e8e37b feat(im): 增加群消息的置顶 2026-05-03 12:15:39 +08:00
YunaiV ffb69063b9 feat(im): 重构群通知相关,对齐 openim 的消息编号(继续优化代码) 2026-05-03 09:22:53 +08:00
YunaiV 43372c05ad feat(im): 重构群通知相关,对齐 openim 的消息编号 2026-05-03 02:00:43 +08:00
YunaiV fa27c27831 feat(im): 增加群角色(管理员) 2026-05-02 14:31:42 +08:00
YunaiV 43666dc56c feat(im): 优化【消息引用】的功能,增加文件、图片的展示调整 2026-05-01 19:05:57 +08:00
YunaiV 1dfab43b8a feat(im): 增加【消息引用】的功能 2026-05-01 18:03:05 +08:00
YunaiV 238862b572 feat(im): 增加发送草稿,切换对话的时候,不丢失。对齐微信 2026-05-01 07:52:18 +08:00
YunaiV dfbae06afa feat(im): 优化输入框的样式 2026-05-01 06:59:14 +08:00
YunaiV 9f1fc9ef78 reviewed 2026-04-30 21:38:17 +08:00
YunaiV fd1ba30bdb feat(im): 优化好友列表的管理 2026-04-30 21:09:03 +08:00
YunaiV d19bdd42d5 feat(im): 优化添加好友界面 2026-04-30 14:53:41 +08:00
YunaiV 4b64153044 feat(im): 完善 friend、group 相关的本地存储(疯狂优化) 2026-04-29 22:03:54 +08:00
YunaiV e90f9e5237 feat(im): 增加 friend、group 相关的本地存储 2026-04-29 15:50:49 +08:00
YunaiV de39bc7fc1 feat(im): 优化代码,移除 message 里的 name 存储,避免更新困难。(为 friend、group 独立存储做准备) 2026-04-28 23:32:40 +08:00
YunaiV bfa267120a ♻️ refactor(im): MessageItem 头像顶右 + MentionPicker/MessageInput 命名清理
【MessageItem.vue】
- 头像合一:双 v-if 头像(左/右)收成单一 <UserAvatar>,DOM 顺序固定为
  [头像, 气泡],selfSend 靠外层 flex-row-reverse 翻视觉 → 头像顶右、气泡在
  头像左侧。早先双 v-if + row-reverse 让自己消息时气泡顶右、头像反而被
  挤在 bubble 左边,跟微信观感不对齐
- 5 处脚本 TODO 注释补齐:groupMembersForReadStatus / handleContextMenu /
  handleRecall / handleDelete,解释 WHY 而不是 WHAT
- formatTipTime 局部变量按"不缩写"展开:d → messageDate / n → value /
  hm → hourMinute / (a,b) → (left,right) / weeks → weekNames
- senderAvatar / groupMembersForReadStatus 回调参数 m → member、g → group

【MessageInput.vue】
- groupMembers producer 局部变量 g → group、(m) => → (member) =>

【MentionPicker.vue】
- memberItems 过滤回调 (m) => → (member) =>
2026-04-27 21:48:34 +08:00
YunaiV 8fd21da555 🐛 fix(im): TIP_TEXT 系统提示不再显示空白
群解散 / 退群 / 踢人 等系统提示后端发的是裸字符串,之前按 TextMessage JSON
解析 → 主聊天窗显示空行、会话列表摘要变空。

- message.ts:新增 resolveTipText helper,兼容裸字符串 + {"content":"..."}
- MessageItem / conversationStore.resolveLastContent 把 TIP_TEXT 从 TEXT
  分支拆出来,统一走 resolveTipText(TEXT 仍按 JSON 解析,没有裸字符串可能)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:59:56 +08:00
YunaiV 9e8d04249c 🐛 fix(im): TIP_TEXT 系统提示不再显示空白
群解散 / 退群 / 踢人 等系统提示后端发的是裸字符串,之前按 TextMessage JSON
解析 → 主聊天窗显示空行、会话列表摘要变空。

- message.ts:新增 resolveTipText helper,兼容裸字符串 + {"content":"..."}
- MessageItem / conversationStore.resolveLastContent 把 TIP_TEXT 从 TEXT
  分支拆出来,统一走 resolveTipText(TEXT 仍按 JSON 解析,没有裸字符串可能)
2026-04-27 19:56:54 +08:00
YunaiV 3ea04663f2 feat(im): IM 5 个 store 补 HMR + 抽 atAll 常量 + 全面补齐 JSDoc
- 全部 5 个 store(conversation / friend / group / ui / websocket)加
  acceptHMRUpdate;Pinia 单例的 actions 是 wrapper 闭包,Vite 推新模块时
  不会自动替换闭包内的旧函数体,导致改 store 后看着热重载、跑的还是旧逻辑
- 抽 IM_AT_ALL_USER_ID(-1)+ IM_AT_ALL_NICKNAME('所有人')到
  utils/constants.ts;conversationStore 删本地 AT_ALL_FLAG 改用共享常量;
  MentionPicker 渲染虚拟项 / ChatGroupMember 类型注释也都引这两个常量
- groupStore.loadGroups 改成合并而非全量替换:用 groupMap 按 id 找已有项,
  保留 loadGroupMembers 写过的 members / memberCount / muted(这三个字段
  不在 ImGroupRespVO 里,全量替换会被冲掉)
- groupStore.loadGroupMembers 重写为分步注释(1. 缓存 / 2. 拉取 /
  3. 回填 muted / 4.1 占位 / 4.2 直写);await 之后必须重新 getGroup
  防 race(loadGroupMembers 与 loadGroups 并发时用入口快照会把真实 name
  覆盖成 String(groupId))
- types/GroupMember 补 muted 字段,convertGroupMember 透传,
  解决 vue-tsc TS2339 / TS2353
- 5 个 store 缺 JSDoc 的方法全部补齐:removePrivateConversation /
  removeGroupConversation / getFriend / getActiveFriends / isFriend /
  loadGroupInfo / upsertGroup / stopHeartbeat
- 全局"墓碑"措辞统一为"软删保留记录",types / friendStore / groupStore 三处
- groupStore 删冗余注释(与代码自描述重复的)若干处;变量 g/old 改 group/existing
2026-04-27 13:10:15 +08:00
YunaiV a1a9053aaa feat(im): 优化 MessagePage.vue 页面,对齐微信交互 2026-04-27 00:51:31 +08:00
YunaiV 66514fc597 ♻️ refactor(im): conversationStore 存储改为 IndexedDB 按会话分桶 + 命名统一
- 持久化迁到 localforage(IndexedDB),meta 索引与单会话 messages 分 key 存,消除写放大
- saveConversations 支持 不传 / 单个 / 数组 三种粒度;签名改为 sync void(fire-and-forget)
- 修复 sortConversations 仅刷 meta 不刷 messages 导致离线消息重启丢失的 bug
- 方法重命名:saveToStorage→saveConversations、updateMessageState→ackMessage、applyRecall→recallMessage、refreshConversations→sortConversations、removeLocalMessage→removeMessage、_removeMessagesStorage→removeConversationMessages
- 删除 dead field Conversation.lastReadCount;TIP_TIME clientMessageId 改用 uuid
2026-04-25 22:52:00 +08:00
YunaiV e30e30ea51 🐛 fix(im): 撤回信号错用 TIP_TEXT,应为 RECALL 2026-04-25 11:42:34 +08:00
YunaiV 68d3ad10d4 feat(im): 优化 im 前端的工具类 2026-04-24 21:36:09 +08:00