From 659e19003d2aff0996a81eb2486cc349cd079013 Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Wed, 2 Apr 2025 17:52:15 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=86=E8=8A=82=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- im-uniapp/.env.js | 3 + im-uniapp/pages/group/group-info.vue | 66 +++++++------- im-uniapp/store/chatStore.js | 6 +- im-web/package.json | 1 + im-web/src/components/chat/ChatGroupSide.vue | 1 - im-web/src/components/friend/FriendItem.vue | 9 +- .../src/components/group/AddGroupMember.vue | 12 +-- im-web/src/store/friendStore.js | 8 -- im-web/src/store/groupStore.js | 10 +-- im-web/src/view/Friend.vue | 85 +++++++++++++++---- im-web/src/view/Group.vue | 65 ++++++++++++-- 11 files changed, 174 insertions(+), 92 deletions(-) diff --git a/im-uniapp/.env.js b/im-uniapp/.env.js index f8238f1..eb43c91 100644 --- a/im-uniapp/.env.js +++ b/im-uniapp/.env.js @@ -2,6 +2,9 @@ const ENV = "DEV"; const UNI_APP = {} +// 每个会话最大消息缓存数量,-1表示不限制 +UNI_APP.MAX_MESSAGE_SIZE = 3000; +// 表情包路径 UNI_APP.EMO_URL = "/static/emoji/"; // #ifdef MP-WEIXIN // 微信小程序的本地表情包经常莫名失效,建议将表情放到服务器中 diff --git a/im-uniapp/pages/group/group-info.vue b/im-uniapp/pages/group/group-info.vue index c487ebc..961a787 100644 --- a/im-uniapp/pages/group/group-info.vue +++ b/im-uniapp/pages/group/group-info.vue @@ -35,16 +35,16 @@ 我在本群的昵称 {{group.showNickName}} - + 群公告 - + - + 修改群聊资料 > - + @@ -103,20 +103,18 @@ export default { url: `/group/quit/${this.groupId}`, method: 'DELETE' }).then(() => { - uni.showModal({ - title: `退出成功`, - content: `您已退出群聊'${this.group.name}'`, - showCancel: false, - success: () => { - setTimeout(() => { - uni.switchTab({ - url: "/pages/group/group" - }); - this.groupStore.removeGroup(this.groupId); - this.chatStore.removeGroupChat(this.groupId); - }, 100) - } + uni.showToast({ + title: `您退出了群聊'${this.group.name}'`, + icon: "none" }) + setTimeout(() => { + uni.switchTab({ + url: "/pages/group/group" + }); + this.groupStore.removeGroup(this.groupId); + this.chatStore.removeGroupChat(this + .groupId); + }, 1500) }); } }); @@ -132,25 +130,21 @@ export default { url: `/group/delete/${this.groupId}`, method: 'delete' }).then(() => { - uni.showModal({ - title: `解散成功`, - content: `群聊'${this.group.name}'已解散`, - showCancel: false, - success: () => { - setTimeout(() => { - uni.switchTab({ - url: "/pages/group/group" - }); - this.groupStore.removeGroup(this.groupId); - this.chatStore.removeGroupChat(this - .groupId); - }, 100) - } + uni.showToast({ + title: `您解散了群聊'${this.group.name}'`, + icon: "none" }) + setTimeout(() => { + uni.switchTab({ + url: "/pages/group/group" + }); + this.groupStore.removeGroup(this.groupId); + this.chatStore.removeGroupChat(this + .groupId); + }, 1500) }); } }); - }, loadGroupInfo() { this.$http({ @@ -255,14 +249,14 @@ export default { background: white; align-items: center; margin-top: 2rpx; - + .label { width: 220rpx; line-height: 100rpx; font-size: $im-font-size; white-space: nowrap; } - + .value { flex: 1; text-align: right; @@ -273,9 +267,9 @@ export default { overflow: hidden; } } - + .group-edit { - padding: 10rpx 40rpx 30rpx 40rpx ; + padding: 10rpx 40rpx 30rpx 40rpx; text-align: center; background: white; font-size: $im-font-size-small; diff --git a/im-uniapp/store/chatStore.js b/im-uniapp/store/chatStore.js index f7aaada..9db36b3 100644 --- a/im-uniapp/store/chatStore.js +++ b/im-uniapp/store/chatStore.js @@ -18,8 +18,12 @@ export default defineStore('chatStore', { cacheChats = []; this.chats = []; for (let chat of chatsData.chats) { - // 暂存至缓冲区 chat.stored = false; + // 清理多余的消息,避免消息过多导致卡顿 + if (UNI_APP.MAX_MESSAGE_SIZE > 0 && chat.messages.length > UNI_APP.MAX_MESSAGE_SIZE) { + chat.messages = chat.messages.slice(0, UNI_APP.MAX_MESSAGE_SIZE); + } + // 暂存至缓冲区 cacheChats.push(JSON.parse(JSON.stringify(chat))); // 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间 if (this.chats.length < 15) { diff --git a/im-web/package.json b/im-web/package.json index 2cf6146..50fcc26 100644 --- a/im-web/package.json +++ b/im-web/package.json @@ -15,6 +15,7 @@ "localforage": "1.10.0", "sass": "1.32.12", "sass-loader": "10.1.1", + "pinyin-pro": "^3.26.0", "vue": "2.7.16", "vue-axios": "3.5.2", "vue-router": "3.6.5", diff --git a/im-web/src/components/chat/ChatGroupSide.vue b/im-web/src/components/chat/ChatGroupSide.vue index 047711a..7c3fe36 100644 --- a/im-web/src/components/chat/ChatGroupSide.vue +++ b/im-web/src/components/chat/ChatGroupSide.vue @@ -109,7 +109,6 @@ export default { method: 'delete' }).then(() => { this.$store.commit("removeGroup", this.group.id); - this.$store.commit("activeGroup", -1); this.$store.commit("removeGroupChat", this.group.id); }); }) diff --git a/im-web/src/components/friend/FriendItem.vue b/im-web/src/components/friend/FriendItem.vue index f77ef3f..b4f3324 100644 --- a/im-web/src/components/friend/FriendItem.vue +++ b/im-web/src/components/friend/FriendItem.vue @@ -63,17 +63,12 @@ export default { this.$emit(item.key.toLowerCase(), this.msgInfo); } }, - computed: { - friend() { - return this.$store.state.friendStore.friends[this.index]; - } - }, props: { active: { type: Boolean }, - index: { - type: Number + friend: { + type: Object }, menu: { type: Boolean, diff --git a/im-web/src/components/group/AddGroupMember.vue b/im-web/src/components/group/AddGroupMember.vue index 84f8a31..85be0c1 100644 --- a/im-web/src/components/group/AddGroupMember.vue +++ b/im-web/src/components/group/AddGroupMember.vue @@ -8,9 +8,9 @@ -
+
+ @click.native="onSwitchCheck(friend)" :menu="false" :friend="friend" :active="false"> @@ -21,9 +21,9 @@
已勾选{{ checkCount }}位好友
-
- +
+
@@ -76,7 +76,7 @@ export default { }) } }, - onRemoveFriend(friend, index) { + onRemoveFriend(friend) { friend.isCheck = false; }, onSwitchCheck(friend) { diff --git a/im-web/src/store/friendStore.js b/im-web/src/store/friendStore.js index 7f81c33..3ba677e 100644 --- a/im-web/src/store/friendStore.js +++ b/im-web/src/store/friendStore.js @@ -5,7 +5,6 @@ export default { state: { friends: [], - activeFriend: null, timer: null }, mutations: { @@ -27,14 +26,8 @@ export default { } }) }, - activeFriend(state, idx) { - state.activeFriend = idx > 0 ? state.friends[idx] : null; - }, removeFriend(state, id) { state.friends.filter(f => f.id == id).forEach(f => f.deleted = true); - if (state.activeFriend && id == state.activeFriend.id) { - state.activeFriend = null; - } }, addFriend(state, friend) { if (state.friends.some((f) => f.id == friend.id)) { @@ -89,7 +82,6 @@ export default { state.timer && clearTimeout(state.timer); state.friends = []; state.timer = null; - state.activeFriend = []; } }, actions: { diff --git a/im-web/src/store/groupStore.js b/im-web/src/store/groupStore.js index 1fba0f2..f261e47 100644 --- a/im-web/src/store/groupStore.js +++ b/im-web/src/store/groupStore.js @@ -2,16 +2,12 @@ import http from '../api/httpRequest.js' export default { state: { - groups: [], - activeGroup: null, + groups: [] }, mutations: { setGroups(state, groups) { state.groups = groups; }, - activeGroup(state, idx) { - state.activeGroup = idx > 0 ? state.groups[idx] : null; - }, addGroup(state, group) { if (state.groups.some((g) => g.id == group.id)) { this.commit("updateGroup", group) @@ -21,9 +17,6 @@ export default { }, removeGroup(state, id) { state.groups.filter(g => g.id == id).forEach(g => g.quit = true); - if (state.activeGroup && id == state.activeGroup.id) { - state.activeGroup = null; - } }, updateGroup(state, group) { state.groups.forEach((g, idx) => { @@ -35,7 +28,6 @@ export default { }, clear(state) { state.groups = []; - state.activeGroup = null; } }, actions: { diff --git a/im-web/src/view/Friend.vue b/im-web/src/view/Friend.vue index d8c23a7..ea5c4d3 100644 --- a/im-web/src/view/Friend.vue +++ b/im-web/src/view/Friend.vue @@ -10,11 +10,14 @@
-
- - +
+
{{ friendKeys[i] }}
+
+ + +
+
@@ -33,7 +36,8 @@ {{ userInfo.nickName }} - {{ userInfo.sex == 0 ? "男" : "女" }} + {{ userInfo.sex == 0 ? "男" : "女" + }} {{ userInfo.signature }}
@@ -43,7 +47,7 @@ 加为好友 删除好友 + @click="onDelFriend(userInfo)">删除好友
@@ -56,6 +60,7 @@ import FriendItem from "../components/friend/FriendItem.vue"; import AddFriend from "../components/friend/AddFriend.vue"; import HeadImage from "../components/common/HeadImage.vue"; +import { pinyin } from 'pinyin-pro'; export default { name: "friend", @@ -69,9 +74,8 @@ export default { return { searchText: "", showAddFriend: false, - activeIdx: -1, userInfo: {}, - friend: {} + activeFriend: {} } }, methods: { @@ -81,13 +85,11 @@ export default { onCloseAddFriend() { this.showAddFriend = false; }, - onActiveItem(friend, idx) { - this.$store.commit("activeFriend", idx); - this.activeIdx = idx - this.friend = friend; + onActiveItem(friend) { + this.activeFriend = friend; this.loadUserInfo(friend.id); }, - onDelItem(friend) { + onDelFriend(friend) { this.$confirm(`确认删除'${friend.nickName}',并清空聊天记录吗?`, '确认解除?', { confirmButtonText: '确定', cancelButtonText: '取消', @@ -128,7 +130,7 @@ export default { showName: user.nickName, headImage: user.headImageThumb, }; - console.log("chat:",chat) + console.log("chat:", chat) this.$store.commit("openChat", chat); this.$store.commit("activeChat", 0); this.$router.push("/home/chat"); @@ -141,7 +143,7 @@ export default { updateFriendInfo() { if (this.isFriend) { // store的数据不能直接修改,深拷贝一份store的数据 - let friend = JSON.parse(JSON.stringify(this.friend)); + let friend = JSON.parse(JSON.stringify(this.activeFriend)); friend.headImage = this.userInfo.headImageThumb; friend.nickName = this.userInfo.nickName; this.$store.commit("updateChatFromFriend", friend); @@ -157,6 +159,18 @@ export default { this.userInfo = userInfo; this.updateFriendInfo(); }) + }, + firstLetter(strText) { + // 使用pinyin-pro库将中文转换为拼音 + let pinyinOptions = { + toneType: 'none', // 无声调 + type: 'normal' // 普通拼音 + }; + let pyText = pinyin(strText, pinyinOptions); + return pyText[0]; + }, + isEnglish(character) { + return /^[A-Za-z]+$/.test(character); } }, computed: { @@ -165,6 +179,45 @@ export default { }, isFriend() { return this.$store.getters.isFriend(this.userInfo.id); + }, + friendMap() { + // 按首字母分组 + let map = new Map(); + this.friendStore.friends.forEach((f) => { + if (f.deleted || (this.searchText && !f.showNickName.includes(this.searchText))) { + return; + } + let letter = this.firstLetter(f.showNickName).toUpperCase(); + // 非英文一律为#组 + if (!this.isEnglish(letter)) { + letter = "#" + } + if (f.online) { + letter = '在线' + } + if (map.has(letter)) { + map.get(letter).push(f); + } else { + map.set(letter, [f]); + } + }) + // 排序 + let arrayObj = Array.from(map); + arrayObj.sort((a, b) => { + // #组在最后面 + if (a[0] == '#' || b[0] == '#') { + return b[0].localeCompare(a[0]) + } + return a[0].localeCompare(b[0]) + }) + map = new Map(arrayObj.map(i => [i[0], i[1]])); + return map; + }, + friendKeys() { + return Array.from(this.friendMap.keys()); + }, + friendValues() { + return Array.from(this.friendMap.values()); } } } diff --git a/im-web/src/view/Group.vue b/im-web/src/view/Group.vue index 683d1c2..a152d6f 100644 --- a/im-web/src/view/Group.vue +++ b/im-web/src/view/Group.vue @@ -8,10 +8,13 @@ -
- - +
+
{{ groupKeys[i] }}
+
+ + +
+
@@ -91,6 +94,7 @@ import FileUpload from '../components/common/FileUpload'; import GroupMember from '../components/group/GroupMember.vue'; import AddGroupMember from '../components/group/AddGroupMember.vue'; import HeadImage from '../components/common/HeadImage.vue'; +import { pinyin } from 'pinyin-pro'; export default { name: "group", @@ -138,8 +142,7 @@ export default { }) }) }, - onActiveItem(group, index) { - this.$store.commit("activeGroup", index); + onActiveItem(group) { // store数据不能直接修改,所以深拷贝一份内存 this.activeGroup = JSON.parse(JSON.stringify(group)); // 重新加载群成员 @@ -182,7 +185,6 @@ export default { }).then(() => { this.$message.success(`群聊'${this.activeGroup.name}'已解散`); this.$store.commit("removeGroup", this.activeGroup.id); - this.$store.commit("removeGroupChat", this.activeGroup.id); this.reset(); }); }) @@ -223,7 +225,6 @@ export default { this.reset(); }); }) - }, onSendMessage() { let chat = { @@ -247,6 +248,18 @@ export default { reset() { this.activeGroup = {}; this.groupMembers = []; + }, + firstLetter(strText) { + // 使用pinyin-pro库将中文转换为拼音 + let pinyinOptions = { + toneType: 'none', // 无声调 + type: 'normal' // 普通拼音 + }; + let pyText = pinyin(strText, pinyinOptions); + return pyText[0]; + }, + isEnglish(character) { + return /^[A-Za-z]+$/.test(character); } }, computed: { @@ -262,6 +275,42 @@ export default { }, imageAction() { return `/image/upload`; + }, + groupMap() { + // 按首字母分组 + let map = new Map(); + this.groupStore.groups.forEach((g) => { + if (g.quit || (this.searchText && !g.showGroupName.includes(this.searchText))) { + return; + } + let letter = this.firstLetter(g.showGroupName).toUpperCase(); + // 非英文一律为#组 + if (!this.isEnglish(letter)) { + letter = "#" + } + if (map.has(letter)) { + map.get(letter).push(g); + } else { + map.set(letter, [g]); + } + }) + // 排序 + let arrayObj = Array.from(map); + arrayObj.sort((a, b) => { + // #组在最后面 + if (a[0] == '#' || b[0] == '#') { + return b[0].localeCompare(a[0]) + } + return a[0].localeCompare(b[0]) + }) + map = new Map(arrayObj.map(i => [i[0], i[1]])); + return map; + }, + groupKeys() { + return Array.from(this.groupMap.keys()); + }, + groupValues() { + return Array.from(this.groupMap.values()); } } }