From 40ac2daca8d6a6e1b9bbd5a13fc43401f79f7799 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 8 May 2026 01:23:09 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(im)=EF=BC=9A=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=B0=94=E6=B3=A1=20@=20=E9=AB=98=E4=BA=AE=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=82=B9=E5=87=BB=20+=20URL=20=E8=87=AA=E5=8A=A8=E8=AF=86?= =?UTF-8?q?=E5=88=AB=E6=88=90=E5=8F=AF=E7=82=B9=E5=87=BB=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - @ 段:群消息按 atUserIds 反查群成员,候选 name 兼容历史字面量(真实昵称 / 好友备注 / 群自定义昵称),displayName 统一收敛到 nickname,让历史消息也能渲染成 @真实昵称;@ 段点击弹 UserInfoCard - @所有人:注入 IM_AT_ALL_USER_ID 虚拟候选,对齐微信 PC 仅高亮配色不挂点击 - 同名歧义:同字面量对应多个 userId 时标记 ambiguous,parser 整段消费成普通文本,避免错绑用户 - URL:识别 http(s) / www. 起头链接, 新标签打开;默认补 https:// - TipSegment 加 link 变体作为统一文本片段类型,TEXT 气泡与灰条 tip 共用 TipSegments 组件渲染 - MessageInput @ token 文本统一用真实昵称,不再掺好友备注 / 群自定义昵称 --- .../components/input/MessageInput.vue | 3 +- .../components/message/MessageBubble.vue | 18 ++- .../components/message/MessageItem.vue | 11 ++ .../components/message/TipSegments.vue | 59 ++++----- src/views/im/utils/message.ts | 109 +++++++++++++++- src/views/im/utils/user.ts | 118 +++++++++++++++++- 6 files changed, 276 insertions(+), 42 deletions(-) diff --git a/src/views/im/home/pages/conversation/components/input/MessageInput.vue b/src/views/im/home/pages/conversation/components/input/MessageInput.vue index 737c51127..8cb1fa077 100644 --- a/src/views/im/home/pages/conversation/components/input/MessageInput.vue +++ b/src/views/im/home/pages/conversation/components/input/MessageInput.vue @@ -739,12 +739,13 @@ function onMentionSelect(member: GroupMemberLite) { } // 删 @keyword,插入 contenteditable=false 的 token: // 删除时整段消除 + 不会被光标拆穿;data-id 是后续 collectFromEditor 收 atUserIds 的钩子 + // token 文本固定走真实昵称:群里所有成员看到的字面量一致,避免我侧的好友备注 / 群昵称污染发送文本 mentionRange.deleteContents() const span = document.createElement('span') span.className = 'mention-token' span.dataset.id = String(member.userId) span.contentEditable = 'false' - span.textContent = `@${member.showName}` + span.textContent = `@${member.nickname || member.showName}` mentionRange.insertNode(span) // token 在 editor 首位时,contenteditable=false 边缘会让光标无法挪到 token 前 // 补一个零宽空格 ​ 当锚点;DOM walk 时会被滤掉,不进入发送内容 diff --git a/src/views/im/home/pages/conversation/components/message/MessageBubble.vue b/src/views/im/home/pages/conversation/components/message/MessageBubble.vue index c4de65ce9..a148a2f89 100644 --- a/src/views/im/home/pages/conversation/components/message/MessageBubble.vue +++ b/src/views/im/home/pages/conversation/components/message/MessageBubble.vue @@ -1,11 +1,11 @@