Browse Source

细节优化

master
xsx 1 year ago
parent
commit
659e19003d
  1. 3
      im-uniapp/.env.js
  2. 36
      im-uniapp/pages/group/group-info.vue
  3. 6
      im-uniapp/store/chatStore.js
  4. 1
      im-web/package.json
  5. 1
      im-web/src/components/chat/ChatGroupSide.vue
  6. 9
      im-web/src/components/friend/FriendItem.vue
  7. 12
      im-web/src/components/group/AddGroupMember.vue
  8. 8
      im-web/src/store/friendStore.js
  9. 10
      im-web/src/store/groupStore.js
  10. 83
      im-web/src/view/Friend.vue
  11. 63
      im-web/src/view/Group.vue

3
im-uniapp/.env.js

@ -2,6 +2,9 @@
const ENV = "DEV"; const ENV = "DEV";
const UNI_APP = {} const UNI_APP = {}
// 每个会话最大消息缓存数量,-1表示不限制
UNI_APP.MAX_MESSAGE_SIZE = 3000;
// 表情包路径
UNI_APP.EMO_URL = "/static/emoji/"; UNI_APP.EMO_URL = "/static/emoji/";
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
// 微信小程序的本地表情包经常莫名失效,建议将表情放到服务器中 // 微信小程序的本地表情包经常莫名失效,建议将表情放到服务器中

36
im-uniapp/pages/group/group-info.vue

@ -35,10 +35,10 @@
<view class="label">我在本群的昵称</view> <view class="label">我在本群的昵称</view>
<view class="value">{{group.showNickName}}</view> <view class="value">{{group.showNickName}}</view>
</view> </view>
<view v-if="group.notice" class="form-item" > <view v-if="group.notice" class="form-item">
<view class="label">群公告</view> <view class="label">群公告</view>
</view> </view>
<view v-if="group.notice" class="form-item" > <view v-if="group.notice" class="form-item">
<uni-notice-bar :text="group.notice" /> <uni-notice-bar :text="group.notice" />
</view> </view>
@ -103,20 +103,18 @@ export default {
url: `/group/quit/${this.groupId}`, url: `/group/quit/${this.groupId}`,
method: 'DELETE' method: 'DELETE'
}).then(() => { }).then(() => {
uni.showModal({ uni.showToast({
title: `退出成功`, title: `您退出了群聊'${this.group.name}'`,
content: `您已退出群聊'${this.group.name}'`, icon: "none"
showCancel: false, })
success: () => {
setTimeout(() => { setTimeout(() => {
uni.switchTab({ uni.switchTab({
url: "/pages/group/group" url: "/pages/group/group"
}); });
this.groupStore.removeGroup(this.groupId); this.groupStore.removeGroup(this.groupId);
this.chatStore.removeGroupChat(this.groupId); this.chatStore.removeGroupChat(this
}, 100) .groupId);
} }, 1500)
})
}); });
} }
}); });
@ -132,11 +130,10 @@ export default {
url: `/group/delete/${this.groupId}`, url: `/group/delete/${this.groupId}`,
method: 'delete' method: 'delete'
}).then(() => { }).then(() => {
uni.showModal({ uni.showToast({
title: `解散成功`, title: `您解散了群聊'${this.group.name}'`,
content: `群聊'${this.group.name}'已解散`, icon: "none"
showCancel: false, })
success: () => {
setTimeout(() => { setTimeout(() => {
uni.switchTab({ uni.switchTab({
url: "/pages/group/group" url: "/pages/group/group"
@ -144,13 +141,10 @@ export default {
this.groupStore.removeGroup(this.groupId); this.groupStore.removeGroup(this.groupId);
this.chatStore.removeGroupChat(this this.chatStore.removeGroupChat(this
.groupId); .groupId);
}, 100) }, 1500)
}
})
}); });
} }
}); });
}, },
loadGroupInfo() { loadGroupInfo() {
this.$http({ this.$http({
@ -275,7 +269,7 @@ export default {
} }
.group-edit { .group-edit {
padding: 10rpx 40rpx 30rpx 40rpx ; padding: 10rpx 40rpx 30rpx 40rpx;
text-align: center; text-align: center;
background: white; background: white;
font-size: $im-font-size-small; font-size: $im-font-size-small;

6
im-uniapp/store/chatStore.js

@ -18,8 +18,12 @@ export default defineStore('chatStore', {
cacheChats = []; cacheChats = [];
this.chats = []; this.chats = [];
for (let chat of chatsData.chats) { for (let chat of chatsData.chats) {
// 暂存至缓冲区
chat.stored = false; 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))); cacheChats.push(JSON.parse(JSON.stringify(chat)));
// 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间 // 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间
if (this.chats.length < 15) { if (this.chats.length < 15) {

1
im-web/package.json

@ -15,6 +15,7 @@
"localforage": "1.10.0", "localforage": "1.10.0",
"sass": "1.32.12", "sass": "1.32.12",
"sass-loader": "10.1.1", "sass-loader": "10.1.1",
"pinyin-pro": "^3.26.0",
"vue": "2.7.16", "vue": "2.7.16",
"vue-axios": "3.5.2", "vue-axios": "3.5.2",
"vue-router": "3.6.5", "vue-router": "3.6.5",

1
im-web/src/components/chat/ChatGroupSide.vue

@ -109,7 +109,6 @@ export default {
method: 'delete' method: 'delete'
}).then(() => { }).then(() => {
this.$store.commit("removeGroup", this.group.id); this.$store.commit("removeGroup", this.group.id);
this.$store.commit("activeGroup", -1);
this.$store.commit("removeGroupChat", this.group.id); this.$store.commit("removeGroupChat", this.group.id);
}); });
}) })

9
im-web/src/components/friend/FriendItem.vue

@ -63,17 +63,12 @@ export default {
this.$emit(item.key.toLowerCase(), this.msgInfo); this.$emit(item.key.toLowerCase(), this.msgInfo);
} }
}, },
computed: {
friend() {
return this.$store.state.friendStore.friends[this.index];
}
},
props: { props: {
active: { active: {
type: Boolean type: Boolean
}, },
index: { friend: {
type: Number type: Object
}, },
menu: { menu: {
type: Boolean, type: Boolean,

12
im-web/src/components/group/AddGroupMember.vue

@ -8,9 +8,9 @@
</el-input> </el-input>
</div> </div>
<el-scrollbar style="height:400px;"> <el-scrollbar style="height:400px;">
<div v-for="(friend, index) in friends" :key="friend.id"> <div v-for="friend in friends" :key="friend.id">
<friend-item v-show="friend.nickName.includes(searchText)" :showDelete="false" <friend-item v-show="friend.nickName.includes(searchText)" :showDelete="false"
@click.native="onSwitchCheck(friend)" :menu="false" :friend="friend" :index="index" :active="false"> @click.native="onSwitchCheck(friend)" :menu="false" :friend="friend" :active="false">
<el-checkbox :disabled="friend.disabled" @click.native.stop="" class="agm-friend-checkbox" <el-checkbox :disabled="friend.disabled" @click.native.stop="" class="agm-friend-checkbox"
v-model="friend.isCheck" size="medium"></el-checkbox> v-model="friend.isCheck" size="medium"></el-checkbox>
</friend-item> </friend-item>
@ -21,9 +21,9 @@
<div class="agm-r-box"> <div class="agm-r-box">
<div class="agm-select-tip"> 已勾选{{ checkCount }}位好友</div> <div class="agm-select-tip"> 已勾选{{ checkCount }}位好友</div>
<el-scrollbar style="height:400px;"> <el-scrollbar style="height:400px;">
<div v-for="(friend, index) in friends" :key="friend.id"> <div v-for="friend in friends" :key="friend.id">
<friend-item v-if="friend.isCheck && !friend.disabled" :friend="friend" :index="index" :active="false" <friend-item v-if="friend.isCheck && !friend.disabled" :friend="friend" :active="false"
@del="onRemoveFriend(friend, index)" :menu="false"> @del="onRemoveFriend(friend)" :menu="false">
</friend-item> </friend-item>
</div> </div>
</el-scrollbar> </el-scrollbar>
@ -76,7 +76,7 @@ export default {
}) })
} }
}, },
onRemoveFriend(friend, index) { onRemoveFriend(friend) {
friend.isCheck = false; friend.isCheck = false;
}, },
onSwitchCheck(friend) { onSwitchCheck(friend) {

8
im-web/src/store/friendStore.js

@ -5,7 +5,6 @@ export default {
state: { state: {
friends: [], friends: [],
activeFriend: null,
timer: null timer: null
}, },
mutations: { mutations: {
@ -27,14 +26,8 @@ export default {
} }
}) })
}, },
activeFriend(state, idx) {
state.activeFriend = idx > 0 ? state.friends[idx] : null;
},
removeFriend(state, id) { removeFriend(state, id) {
state.friends.filter(f => f.id == id).forEach(f => f.deleted = true); 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) { addFriend(state, friend) {
if (state.friends.some((f) => f.id == friend.id)) { if (state.friends.some((f) => f.id == friend.id)) {
@ -89,7 +82,6 @@ export default {
state.timer && clearTimeout(state.timer); state.timer && clearTimeout(state.timer);
state.friends = []; state.friends = [];
state.timer = null; state.timer = null;
state.activeFriend = [];
} }
}, },
actions: { actions: {

10
im-web/src/store/groupStore.js

@ -2,16 +2,12 @@ import http from '../api/httpRequest.js'
export default { export default {
state: { state: {
groups: [], groups: []
activeGroup: null,
}, },
mutations: { mutations: {
setGroups(state, groups) { setGroups(state, groups) {
state.groups = groups; state.groups = groups;
}, },
activeGroup(state, idx) {
state.activeGroup = idx > 0 ? state.groups[idx] : null;
},
addGroup(state, group) { addGroup(state, group) {
if (state.groups.some((g) => g.id == group.id)) { if (state.groups.some((g) => g.id == group.id)) {
this.commit("updateGroup", group) this.commit("updateGroup", group)
@ -21,9 +17,6 @@ export default {
}, },
removeGroup(state, id) { removeGroup(state, id) {
state.groups.filter(g => g.id == id).forEach(g => g.quit = true); 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) { updateGroup(state, group) {
state.groups.forEach((g, idx) => { state.groups.forEach((g, idx) => {
@ -35,7 +28,6 @@ export default {
}, },
clear(state) { clear(state) {
state.groups = []; state.groups = [];
state.activeGroup = null;
} }
}, },
actions: { actions: {

83
im-web/src/view/Friend.vue

@ -10,12 +10,15 @@
<add-friend :dialogVisible="showAddFriend" @close="onCloseAddFriend"></add-friend> <add-friend :dialogVisible="showAddFriend" @close="onCloseAddFriend"></add-friend>
</div> </div>
<el-scrollbar class="friend-list-items"> <el-scrollbar class="friend-list-items">
<div v-for="(friend, index) in $store.state.friendStore.friends" :key="index"> <div v-for="(friends, i) in friendValues" :key="i">
<friend-item v-if="!friend.deleted" v-show="friend.nickName.includes(searchText)" :index="index" <div class="index-title">{{ friendKeys[i] }}</div>
:active="friend === $store.state.friendStore.activeFriend" @chat="onSendMessage(friend)" <div v-for="(friend) in friends" :key="friend.id">
@delete="onDelItem(friend)" @click.native="onActiveItem(friend, index)"> <friend-item :friend="friend" :active="friend.id === activeFriend.id" @chat="onSendMessage(friend)"
@delete="onDelFriend(friend)" @click.native="onActiveItem(friend)">
</friend-item> </friend-item>
</div> </div>
<div v-if="i < friendValues.length - 1" class="divider"></div>
</div>
</el-scrollbar> </el-scrollbar>
</el-aside> </el-aside>
<el-container class="friend-box"> <el-container class="friend-box">
@ -33,7 +36,8 @@
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="昵称">{{ userInfo.nickName }} <el-descriptions-item label="昵称">{{ userInfo.nickName }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="性别">{{ userInfo.sex == 0 ? "男" : "女" }}</el-descriptions-item> <el-descriptions-item label="性别">{{ userInfo.sex == 0 ? "男" : "女"
}}</el-descriptions-item>
<el-descriptions-item label="签名">{{ userInfo.signature }}</el-descriptions-item> <el-descriptions-item label="签名">{{ userInfo.signature }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
</div> </div>
@ -43,7 +47,7 @@
<el-button v-show="!isFriend" icon="el-icon-plus" type="primary" <el-button v-show="!isFriend" icon="el-icon-plus" type="primary"
@click="onAddFriend(userInfo)">加为好友</el-button> @click="onAddFriend(userInfo)">加为好友</el-button>
<el-button v-show="isFriend" icon="el-icon-delete" type="danger" <el-button v-show="isFriend" icon="el-icon-delete" type="danger"
@click="onDelItem(userInfo, activeIdx)">删除好友</el-button> @click="onDelFriend(userInfo)">删除好友</el-button>
</div> </div>
</div> </div>
</div> </div>
@ -56,6 +60,7 @@
import FriendItem from "../components/friend/FriendItem.vue"; import FriendItem from "../components/friend/FriendItem.vue";
import AddFriend from "../components/friend/AddFriend.vue"; import AddFriend from "../components/friend/AddFriend.vue";
import HeadImage from "../components/common/HeadImage.vue"; import HeadImage from "../components/common/HeadImage.vue";
import { pinyin } from 'pinyin-pro';
export default { export default {
name: "friend", name: "friend",
@ -69,9 +74,8 @@ export default {
return { return {
searchText: "", searchText: "",
showAddFriend: false, showAddFriend: false,
activeIdx: -1,
userInfo: {}, userInfo: {},
friend: {} activeFriend: {}
} }
}, },
methods: { methods: {
@ -81,13 +85,11 @@ export default {
onCloseAddFriend() { onCloseAddFriend() {
this.showAddFriend = false; this.showAddFriend = false;
}, },
onActiveItem(friend, idx) { onActiveItem(friend) {
this.$store.commit("activeFriend", idx); this.activeFriend = friend;
this.activeIdx = idx
this.friend = friend;
this.loadUserInfo(friend.id); this.loadUserInfo(friend.id);
}, },
onDelItem(friend) { onDelFriend(friend) {
this.$confirm(`确认删除'${friend.nickName}',并清空聊天记录吗?`, '确认解除?', { this.$confirm(`确认删除'${friend.nickName}',并清空聊天记录吗?`, '确认解除?', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
@ -128,7 +130,7 @@ export default {
showName: user.nickName, showName: user.nickName,
headImage: user.headImageThumb, headImage: user.headImageThumb,
}; };
console.log("chat:",chat) console.log("chat:", chat)
this.$store.commit("openChat", chat); this.$store.commit("openChat", chat);
this.$store.commit("activeChat", 0); this.$store.commit("activeChat", 0);
this.$router.push("/home/chat"); this.$router.push("/home/chat");
@ -141,7 +143,7 @@ export default {
updateFriendInfo() { updateFriendInfo() {
if (this.isFriend) { if (this.isFriend) {
// storestore // storestore
let friend = JSON.parse(JSON.stringify(this.friend)); let friend = JSON.parse(JSON.stringify(this.activeFriend));
friend.headImage = this.userInfo.headImageThumb; friend.headImage = this.userInfo.headImageThumb;
friend.nickName = this.userInfo.nickName; friend.nickName = this.userInfo.nickName;
this.$store.commit("updateChatFromFriend", friend); this.$store.commit("updateChatFromFriend", friend);
@ -157,6 +159,18 @@ export default {
this.userInfo = userInfo; this.userInfo = userInfo;
this.updateFriendInfo(); 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: { computed: {
@ -165,6 +179,45 @@ export default {
}, },
isFriend() { isFriend() {
return this.$store.getters.isFriend(this.userInfo.id); 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());
} }
} }
} }

63
im-web/src/view/Group.vue

@ -8,11 +8,14 @@
<el-button plain class="add-btn" icon="el-icon-plus" title="创建群聊" @click="onCreateGroup()"></el-button> <el-button plain class="add-btn" icon="el-icon-plus" title="创建群聊" @click="onCreateGroup()"></el-button>
</div> </div>
<el-scrollbar class="group-list-items"> <el-scrollbar class="group-list-items">
<div v-for="(group, index) in groupStore.groups" :key="index"> <div v-for="(groups, i) in groupValues" :key="i">
<group-item v-show="!group.quit && group.showGroupName.includes(searchText)" :group="group" <div class="index-title">{{ groupKeys[i] }}</div>
:active="group === groupStore.activeGroup" @click.native="onActiveItem(group, index)"> <div v-for="group in groups" :key="group.id">
<group-item :group="group" :active="group.id == activeGroup.id" @click.native="onActiveItem(group)">
</group-item> </group-item>
</div> </div>
<div v-if="i < groupValues.length - 1" class="divider"></div>
</div>
</el-scrollbar> </el-scrollbar>
</el-aside> </el-aside>
<el-container class="group-box"> <el-container class="group-box">
@ -91,6 +94,7 @@ import FileUpload from '../components/common/FileUpload';
import GroupMember from '../components/group/GroupMember.vue'; import GroupMember from '../components/group/GroupMember.vue';
import AddGroupMember from '../components/group/AddGroupMember.vue'; import AddGroupMember from '../components/group/AddGroupMember.vue';
import HeadImage from '../components/common/HeadImage.vue'; import HeadImage from '../components/common/HeadImage.vue';
import { pinyin } from 'pinyin-pro';
export default { export default {
name: "group", name: "group",
@ -138,8 +142,7 @@ export default {
}) })
}) })
}, },
onActiveItem(group, index) { onActiveItem(group) {
this.$store.commit("activeGroup", index);
// store // store
this.activeGroup = JSON.parse(JSON.stringify(group)); this.activeGroup = JSON.parse(JSON.stringify(group));
// //
@ -182,7 +185,6 @@ export default {
}).then(() => { }).then(() => {
this.$message.success(`群聊'${this.activeGroup.name}'已解散`); this.$message.success(`群聊'${this.activeGroup.name}'已解散`);
this.$store.commit("removeGroup", this.activeGroup.id); this.$store.commit("removeGroup", this.activeGroup.id);
this.$store.commit("removeGroupChat", this.activeGroup.id);
this.reset(); this.reset();
}); });
}) })
@ -223,7 +225,6 @@ export default {
this.reset(); this.reset();
}); });
}) })
}, },
onSendMessage() { onSendMessage() {
let chat = { let chat = {
@ -247,6 +248,18 @@ export default {
reset() { reset() {
this.activeGroup = {}; this.activeGroup = {};
this.groupMembers = []; 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: { computed: {
@ -262,6 +275,42 @@ export default {
}, },
imageAction() { imageAction() {
return `/image/upload`; 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());
} }
} }
} }

Loading…
Cancel
Save