Browse Source

细节优化

master
xsx 1 year ago
parent
commit
659e19003d
  1. 3
      im-uniapp/.env.js
  2. 56
      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. 85
      im-web/src/view/Friend.vue
  11. 65
      im-web/src/view/Group.vue

3
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
// 微信小程序的本地表情包经常莫名失效,建议将表情放到服务器中

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

@ -35,10 +35,10 @@
<view class="label">我在本群的昵称</view>
<view class="value">{{group.showNickName}}</view>
</view>
<view v-if="group.notice" class="form-item" >
<view v-if="group.notice" class="form-item">
<view class="label">群公告</view>
</view>
<view v-if="group.notice" class="form-item" >
<view v-if="group.notice" class="form-item">
<uni-notice-bar :text="group.notice" />
</view>
@ -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({
@ -275,7 +269,7 @@ export default {
}
.group-edit {
padding: 10rpx 40rpx 30rpx 40rpx ;
padding: 10rpx 40rpx 30rpx 40rpx;
text-align: center;
background: white;
font-size: $im-font-size-small;

6
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) {

1
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",

1
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);
});
})

9
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,

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

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

8
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: {

10
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: {

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

@ -10,11 +10,14 @@
<add-friend :dialogVisible="showAddFriend" @close="onCloseAddFriend"></add-friend>
</div>
<el-scrollbar class="friend-list-items">
<div v-for="(friend, index) in $store.state.friendStore.friends" :key="index">
<friend-item v-if="!friend.deleted" v-show="friend.nickName.includes(searchText)" :index="index"
:active="friend === $store.state.friendStore.activeFriend" @chat="onSendMessage(friend)"
@delete="onDelItem(friend)" @click.native="onActiveItem(friend, index)">
</friend-item>
<div v-for="(friends, i) in friendValues" :key="i">
<div class="index-title">{{ friendKeys[i] }}</div>
<div v-for="(friend) in friends" :key="friend.id">
<friend-item :friend="friend" :active="friend.id === activeFriend.id" @chat="onSendMessage(friend)"
@delete="onDelFriend(friend)" @click.native="onActiveItem(friend)">
</friend-item>
</div>
<div v-if="i < friendValues.length - 1" class="divider"></div>
</div>
</el-scrollbar>
</el-aside>
@ -33,7 +36,8 @@
</el-descriptions-item>
<el-descriptions-item label="昵称">{{ userInfo.nickName }}
</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>
</div>
@ -43,7 +47,7 @@
<el-button v-show="!isFriend" icon="el-icon-plus" type="primary"
@click="onAddFriend(userInfo)">加为好友</el-button>
<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>
@ -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) {
// storestore
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());
}
}
}

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

@ -8,10 +8,13 @@
<el-button plain class="add-btn" icon="el-icon-plus" title="创建群聊" @click="onCreateGroup()"></el-button>
</div>
<el-scrollbar class="group-list-items">
<div v-for="(group, index) in groupStore.groups" :key="index">
<group-item v-show="!group.quit && group.showGroupName.includes(searchText)" :group="group"
:active="group === groupStore.activeGroup" @click.native="onActiveItem(group, index)">
</group-item>
<div v-for="(groups, i) in groupValues" :key="i">
<div class="index-title">{{ groupKeys[i] }}</div>
<div v-for="group in groups" :key="group.id">
<group-item :group="group" :active="group.id == activeGroup.id" @click.native="onActiveItem(group)">
</group-item>
</div>
<div v-if="i < groupValues.length - 1" class="divider"></div>
</div>
</el-scrollbar>
</el-aside>
@ -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());
}
}
}

Loading…
Cancel
Save