Browse Source

!161 移除default-passive-events插件(会导致h5键盘无法保持)

Merge pull request !161 from blue/v_3.0.0
master
blue 7 months ago
committed by Gitee
parent
commit
4a423d83c8
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
  1. 22
      im-uniapp/App.vue
  2. 12
      im-uniapp/components/chat-message-item/chat-message-item.vue
  3. 7
      im-uniapp/components/nav-bar/nav-bar.vue
  4. 1
      im-uniapp/main.js
  5. 1
      im-uniapp/package.json
  6. 10
      im-uniapp/pages.json
  7. 114
      im-uniapp/pages/chat/chat-box.vue
  8. 10
      im-uniapp/pages/friend/friend.vue
  9. 22
      im-uniapp/store/chatStore.js
  10. 44
      im-uniapp/store/friendStore.js
  11. 30
      im-uniapp/store/groupStore.js
  12. 22
      im-web/src/components/chat/ChatBox.vue
  13. 3
      im-web/src/components/chat/ChatMessageItem.vue
  14. 24
      im-web/src/store/chatStore.js
  15. BIN
      截图/app/1.png
  16. BIN
      截图/app/2.png
  17. BIN
      截图/web/单人通话.jpg
  18. BIN
      截图/web/多人通话.jpg
  19. BIN
      截图/web/好友.jpg
  20. BIN
      截图/web/私聊.jpg
  21. BIN
      截图/web/群列表.jpg
  22. BIN
      截图/web/群聊.jpg
  23. BIN
      截图/消息推送集群化.jpg

22
im-uniapp/App.vue

@ -492,6 +492,9 @@ uni-page-head {
} }
// #endif // #endif
page {
background-color: $im-bg;
}
.tab-page { .tab-page {
position: relative; position: relative;
@ -502,9 +505,15 @@ uni-page-head {
top: $im-nav-bar-height; top: $im-nav-bar-height;
// #endif // #endif
// #ifndef H5 // #ifdef APP-PLUS
height: calc(100vh - var(--status-bar-height) - $im-nav-bar-height); // app height: calc(100vh - var(--status-bar-height) - $im-nav-bar-height); // app
top: calc($im-nav-bar-height + var(--status-bar-height)); top: calc($im-nav-bar-height + var(--status-bar-height));
// #endif
// #ifdef MP-WEIXIN
height: calc(100vh - $im-nav-bar-height);
top: $im-nav-bar-height;
// #endif // #endif
color: $im-text-color; color: $im-text-color;
background-color: $im-bg; background-color: $im-bg;
@ -515,13 +524,20 @@ uni-page-head {
.page { .page {
position: relative; position: relative;
// #ifdef H5 // #ifdef H5
height: calc(100vh - $im-nav-bar-height); // app height: calc(100vh - $im-nav-bar-height); // h5100vh
top: $im-nav-bar-height; top: $im-nav-bar-height;
// #endif // #endif
// #ifndef H5
// #ifdef APP-PLUS
height: calc(100vh - var(--status-bar-height) - $im-nav-bar-height); // app height: calc(100vh - var(--status-bar-height) - $im-nav-bar-height); // app
top: calc($im-nav-bar-height + var(--status-bar-height)); top: calc($im-nav-bar-height + var(--status-bar-height));
// #endif // #endif
// #ifdef MP-WEIXIN
height: calc(100vh - $im-nav-bar-height);
top: $im-nav-bar-height;
// #endif
color: $im-text-color; color: $im-text-color;
background-color: $im-bg; background-color: $im-bg;
font-size: $im-font-size; font-size: $im-font-size;

12
im-uniapp/components/chat-message-item/chat-message-item.vue

@ -56,12 +56,6 @@
<text v-if="audioPlayState == 'PLAYING'" class="iconfont icon-pause"></text> <text v-if="audioPlayState == 'PLAYING'" class="iconfont icon-pause"></text>
</view> </view>
</long-press-menu> </long-press-menu>
<view v-if="sending&&isTextMessage" class="sending">
<loading :size="40" icon-color="#656adf" :mask="false"></loading>
</view>
<view v-else-if="sendFail" @click="onSendFail"
class="send-fail iconfont icon-warning-circle-fill"></view>
</view>
<long-press-menu v-if="isAction" :items="menuItems" @select="onSelectMenu"> <long-press-menu v-if="isAction" :items="menuItems" @select="onSelectMenu">
<view class="chat-realtime message-text" @click="$emit('call')"> <view class="chat-realtime message-text" @click="$emit('call')">
<text v-if="msgInfo.type == $enums.MESSAGE_TYPE.ACT_RT_VOICE" <text v-if="msgInfo.type == $enums.MESSAGE_TYPE.ACT_RT_VOICE"
@ -71,6 +65,12 @@
<text>{{ msgInfo.content }}</text> <text>{{ msgInfo.content }}</text>
</view> </view>
</long-press-menu> </long-press-menu>
<view v-if="sending&&isTextMessage" class="sending">
<loading :size="40" icon-color="#656adf" :mask="false"></loading>
</view>
<view v-else-if="sendFail" @click="onSendFail"
class="send-fail iconfont icon-warning-circle-fill"></view>
</view>
<view class="message-status" v-if="!isAction && msgInfo.selfSend && !msgInfo.groupId"> <view class="message-status" v-if="!isAction && msgInfo.selfSend && !msgInfo.groupId">
<text class="chat-readed" v-if="msgInfo.status == $enums.MESSAGE_STATUS.READED">已读</text> <text class="chat-readed" v-if="msgInfo.status == $enums.MESSAGE_STATUS.READED">已读</text>
<text class="chat-unread" v-else>未读</text> <text class="chat-unread" v-else>未读</text>

7
im-uniapp/components/nav-bar/nav-bar.vue

@ -1,19 +1,22 @@
<template> <template>
<view class="im-nav-bar"> <view class="im-nav-bar">
<!-- #ifndef H5 --> <!-- #ifdef APP-PLUS -->
<view style="height: var(--status-bar-height)"></view> <view style="height: var(--status-bar-height)"></view>
<!-- #endif --> <!-- #endif -->
<view class="im-nav-bar-content"> <view class="im-nav-bar-content">
<!-- #ifndef MP-WEIXIN -->
<view class="back" @click="handleBackClick" v-if="back"> <view class="back" @click="handleBackClick" v-if="back">
<uni-icons type="back" :size="iconFontSize"></uni-icons> <uni-icons type="back" :size="iconFontSize"></uni-icons>
</view> </view>
<!-- #endif -->
<view class="title" v-if="title"> <view class="title" v-if="title">
<slot></slot> <slot></slot>
</view> </view>
<view class="btn"> <view class="btn">
<uni-icons class="btn-item" v-if="search" type="search" :size="iconFontSize" <uni-icons class="btn-item" v-if="search" type="search" :size="iconFontSize"
@click="$emit('search')"></uni-icons> @click="$emit('search')"></uni-icons>
<uni-icons class="btn-item" v-if="add" type="plusempty" :size="iconFontSize" @click="$emit('add')"></uni-icons> <uni-icons class="btn-item" v-if="add" type="plusempty" :size="iconFontSize"
@click="$emit('add')"></uni-icons>
<uni-icons class="btn-item" v-if="more" type="more-filled" :size="iconFontSize" <uni-icons class="btn-item" v-if="more" type="more-filled" :size="iconFontSize"
@click="$emit('more')"></uni-icons> @click="$emit('more')"></uni-icons>
</view> </view>

1
im-uniapp/main.js

@ -23,7 +23,6 @@ import switchBar from '@/components/bar/switch-bar'
import * as recorder from './common/recorder-h5'; import * as recorder from './common/recorder-h5';
import ImageResize from "quill-image-resize-mp"; import ImageResize from "quill-image-resize-mp";
import Quill from "quill"; import Quill from "quill";
import 'default-passive-events';
// 以下组件用于兼容部分手机聊天边框无法输入的问题 // 以下组件用于兼容部分手机聊天边框无法输入的问题
window.Quill = Quill; window.Quill = Quill;
window.ImageResize = { default: ImageResize }; window.ImageResize = { default: ImageResize };

1
im-uniapp/package.json

@ -4,7 +4,6 @@
"scripts": {} "scripts": {}
}, },
"dependencies": { "dependencies": {
"default-passive-events": "^4.0.0",
"pinyin-pro": "^3.23.1", "pinyin-pro": "^3.23.1",
"quill": "^1.3.7", "quill": "^1.3.7",
"quill-image-resize-mp": "^3.0.1", "quill-image-resize-mp": "^3.0.1",

10
im-uniapp/pages.json

@ -30,7 +30,9 @@
{ {
"path": "pages/chat/chat-box", "path": "pages/chat/chat-box",
"style": { "style": {
"navigationStyle": "custom", "mp-weixin": {
"disableScroll": true
},
"app-plus": { "app-plus": {
// adjustPanadjustResize=webview+ // adjustPanadjustResize=webview+
"softinputMode": "adjustResize" "softinputMode": "adjustResize"
@ -67,7 +69,13 @@
], ],
"globalStyle": { "globalStyle": {
"navigationBarTitleText": "盒子IM", "navigationBarTitleText": "盒子IM",
"navigationBarTextStyle": "black",
/* #ifndef MP-WEIXIN */
"navigationStyle": "custom", "navigationStyle": "custom",
/* #endif */
/* #ifdef MP-WEIXIN */
"navigationStyle": "default",
/* #endif */
"navigationBarBackgroundColor": "#f7f7f7", "navigationBarBackgroundColor": "#f7f7f7",
"backgroundColor": "#f7f7f7" "backgroundColor": "#f7f7f7"
}, },

114
im-uniapp/pages/chat/chat-box.vue

@ -50,7 +50,8 @@
</view> </view>
</view> </view>
<view class="chat-tab-bar"> <view class="chat-tab-bar">
<view v-if="chatTabBox == 'tools'" class="chat-tools" :style="{height: keyboardHeight+'px'}"> <scroll-view v-if="chatTabBox == 'tools'" class="chat-tools" :style="{height: keyboardHeight+'px'}">
<view class="chat-tools-list">
<view class="chat-tools-item"> <view class="chat-tools-item">
<file-upload ref="fileUpload" :onBefore="onUploadFileBefore" :onSuccess="onUploadFileSuccess" <file-upload ref="fileUpload" :onBefore="onUploadFileBefore" :onSuccess="onUploadFileSuccess"
:onError="onUploadFileFail"> :onError="onUploadFileFail">
@ -66,8 +67,8 @@
<view class="tool-name">相册</view> <view class="tool-name">相册</view>
</view> </view>
<view class="chat-tools-item"> <view class="chat-tools-item">
<image-upload sourceType="camera" :onBefore="onUploadImageBefore" :onSuccess="onUploadImageSuccess" <image-upload sourceType="camera" :onBefore="onUploadImageBefore"
:onError="onUploadImageFail"> :onSuccess="onUploadImageSuccess" :onError="onUploadImageFail">
<view class="tool-icon iconfont icon-camera"></view> <view class="tool-icon iconfont icon-camera"></view>
</image-upload> </image-upload>
<view class="tool-name">拍摄</view> <view class="tool-name">拍摄</view>
@ -76,7 +77,8 @@
<view class="tool-icon iconfont icon-microphone"></view> <view class="tool-icon iconfont icon-microphone"></view>
<view class="tool-name">语音消息</view> <view class="tool-name">语音消息</view>
</view> </view>
<view v-if="chat.type == 'GROUP' && memberSize<=500" class="chat-tools-item" @click="switchReceipt()"> <view v-if="chat.type == 'GROUP' && memberSize<=500" class="chat-tools-item"
@click="switchReceipt()">
<view class="tool-icon iconfont icon-receipt" :class="isReceipt ? 'active' : ''"></view> <view class="tool-icon iconfont icon-receipt" :class="isReceipt ? 'active' : ''"></view>
<view class="tool-name">回执消息</view> <view class="tool-name">回执消息</view>
</view> </view>
@ -96,6 +98,7 @@
</view> </view>
<!-- #endif --> <!-- #endif -->
</view> </view>
</scroll-view>
<scroll-view v-if="chatTabBox === 'emo'" class="chat-emotion" scroll-y="true" <scroll-view v-if="chatTabBox === 'emo'" class="chat-emotion" scroll-y="true"
:style="{height: keyboardHeight+'px'}"> :style="{height: keyboardHeight+'px'}">
<view class="emotion-item-list"> <view class="emotion-item-list">
@ -133,6 +136,7 @@ export default {
showRecord: false, showRecord: false,
chatMainHeight: 800, // chatMainHeight: 800, //
keyboardHeight: 290, // keyboardHeight: 290, //
screenHeight: 1000, //
windowHeight: 1000, // windowHeight: 1000, //
initHeight: 1000, // h5 initHeight: 1000, // h5
atUserIds: [], atUserIds: [],
@ -184,7 +188,7 @@ export default {
// //
tmpMessage.id = m.id; tmpMessage.id = m.id;
tmpMessage.status = m.status; tmpMessage.status = m.status;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
// //
this.moveChatToTop(); this.moveChatToTop();
// //
@ -331,12 +335,12 @@ export default {
tmpMessage.id = m.id; tmpMessage.id = m.id;
tmpMessage.status = m.status; tmpMessage.status = m.status;
tmpMessage.content = m.content; tmpMessage.content = m.content;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}).catch(() => { }).catch(() => {
// //
tmpMessage = JSON.parse(JSON.stringify(tmpMessage)); tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED; tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}) })
} }
}) })
@ -393,11 +397,13 @@ export default {
this.switchChatTabBox('tools') this.switchChatTabBox('tools')
}, },
switchChatTabBox(chatTabBox) { switchChatTabBox(chatTabBox) {
if (this.chatTabBox != chatTabBox) {
this.chatTabBox = chatTabBox; this.chatTabBox = chatTabBox;
this.reCalChatMainHeight();
if (chatTabBox != 'tools' && this.$refs.fileUpload) { if (chatTabBox != 'tools' && this.$refs.fileUpload) {
this.$refs.fileUpload.hide() this.$refs.fileUpload.hide()
} }
setTimeout(() => this.reCalChatMainHeight(), 30);
}
}, },
selectEmoji(emoText) { selectEmoji(emoText) {
let path = this.$emo.textToPath(emoText) let path = this.$emo.textToPath(emoText)
@ -454,7 +460,7 @@ export default {
data.width = size.width; data.width = size.width;
data.height = size.height; data.height = size.height;
msgInfo.content = JSON.stringify(data) msgInfo.content = JSON.stringify(data)
this.chatStore.insertMessage(msgInfo, chat); this.chatStore.updateMessage(msgInfo, chat);
this.scrollToBottom(); this.scrollToBottom();
}) })
return true; return true;
@ -467,13 +473,13 @@ export default {
msgInfo.id = m.id; msgInfo.id = m.id;
msgInfo.status = m.status; msgInfo.status = m.status;
this.isReceipt = false; this.isReceipt = false;
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}) })
}, },
onUploadImageFail(file, err) { onUploadImageFail(file, err) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}, },
onUploadFileBefore(file) { onUploadFileBefore(file) {
// //
@ -520,13 +526,13 @@ export default {
msgInfo.id = m.id; msgInfo.id = m.id;
msgInfo.status = m.status; msgInfo.status = m.status;
this.isReceipt = false; this.isReceipt = false;
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}) })
}, },
onUploadFileFail(file, res) { onUploadFileFail(file, res) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}, },
onResendMessage(msgInfo) { onResendMessage(msgInfo) {
if (msgInfo.type != this.$enums.MESSAGE_TYPE.TEXT) { if (msgInfo.type != this.$enums.MESSAGE_TYPE.TEXT) {
@ -551,12 +557,12 @@ export default {
tmpMessage.id = m.id; tmpMessage.id = m.id;
tmpMessage.status = m.status; tmpMessage.status = m.status;
tmpMessage.content = m.content; tmpMessage.content = m.content;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}).catch(() => { }).catch(() => {
// //
tmpMessage = JSON.parse(JSON.stringify(tmpMessage)); tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED; tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}) })
}, },
onDeleteMessage(msgInfo) { onDeleteMessage(msgInfo) {
@ -670,9 +676,13 @@ export default {
if (this.scrollTop < 10) { if (this.scrollTop < 10) {
// //
this.holdingScrollBar(); this.holdingScrollBar();
} else {
// ios
const delays = [100, 300, 1000];
delays.forEach(delay => setTimeout(() => this.scrollTop += 5, delay))
} }
}); });
}, 50) }, 10)
}, },
onShowMore() { onShowMore() {
if (this.isGroup) { if (this.isGroup) {
@ -815,25 +825,29 @@ export default {
} }
}, },
reCalChatMainHeight() { reCalChatMainHeight() {
setTimeout(() => { let sysInfo = uni.getSystemInfoSync();
let h = this.windowHeight; let h = this.windowHeight;
// //
h -= 50; h -= 50;
// //
if (this.isShowKeyBoard || this.chatTabBox != 'none') { if (this.isShowKeyBoard || this.chatTabBox != 'none') {
// ios app
// #ifdef APP-PLUS
if (sysInfo.platform == 'ios') {
h += this.screenHeight - this.windowHeight;
}
// #endif
h -= this.keyboardHeight; h -= this.keyboardHeight;
this.scrollToBottom(); this.scrollToBottom();
} }
// #ifndef H5
// h5 // APP
h -= uni.getSystemInfoSync().statusBarHeight; // #ifdef APP-PLUS
h -= sysInfo.statusBarHeight;
// #endif // #endif
this.chatMainHeight = h; this.chatMainHeight = h;
if (this.isShowKeyBoard || this.chatTabBox != 'none') { // #ifndef APP
this.scrollToBottom();
}
// ios // ios
// #ifdef H5
if (uni.getSystemInfoSync().platform == 'ios') { if (uni.getSystemInfoSync().platform == 'ios') {
// //
const delays = [50, 100, 500]; const delays = [50, 100, 500];
@ -847,7 +861,6 @@ export default {
}) })
} }
// #endif // #endif
}, 30)
}, },
listenKeyBoard() { listenKeyBoard() {
// #ifdef H5 // #ifdef H5
@ -857,12 +870,13 @@ export default {
return; return;
} }
if (uni.getSystemInfoSync().platform == 'ios') { if (uni.getSystemInfoSync().platform == 'ios') {
// ios h5
window.addEventListener('focusin', this.focusInListener);
window.addEventListener('focusout', this.focusOutListener);
// ios13 // ios13
if (window.visualViewport) { if (window.visualViewport) {
window.visualViewport.addEventListener('resize', this.resizeListener); window.visualViewport.addEventListener('resize', this.resizeListener);
} else {
// ios h5
window.addEventListener('focusin', this.focusInListener);
window.addEventListener('focusout', this.focusOutListener);
} }
} else { } else {
// h5 // h5
@ -876,9 +890,12 @@ export default {
}, },
unListenKeyboard() { unListenKeyboard() {
// #ifdef H5 // #ifdef H5
window.removeEventListener('resize', this.resizeListener);
window.removeEventListener('focusin', this.focusInListener); window.removeEventListener('focusin', this.focusInListener);
window.removeEventListener('focusout', this.focusOutListener); window.removeEventListener('focusout', this.focusOutListener);
window.removeEventListener('resize', this.resizeListener);
if (window.visualViewport) {
window.visualViewport.removeEventListener('resize', this.resizeListener);
}
// #endif // #endif
// #ifndef H5 // #ifndef H5
uni.offKeyboardHeightChange(this.keyBoardListener); uni.offKeyboardHeightChange(this.keyBoardListener);
@ -889,7 +906,7 @@ export default {
if (this.isShowKeyBoard) { if (this.isShowKeyBoard) {
this.keyboardHeight = res.height; // this.keyboardHeight = res.height; //
} }
this.reCalChatMainHeight(); setTimeout(() => this.reCalChatMainHeight(), 30);
}, },
resizeListener() { resizeListener() {
let keyboardHeight = this.initHeight - window.innerHeight; let keyboardHeight = this.initHeight - window.innerHeight;
@ -897,20 +914,22 @@ export default {
if (window.visualViewport && uni.getSystemInfoSync().platform == 'ios') { if (window.visualViewport && uni.getSystemInfoSync().platform == 'ios') {
keyboardHeight = this.initHeight - window.visualViewport.height; keyboardHeight = this.initHeight - window.visualViewport.height;
} }
console.log("resizeListener:", window.visualViewport.height) let isShowKeyBoard = keyboardHeight > 150;
this.isShowKeyBoard = keyboardHeight > 150; if (isShowKeyBoard) {
if (this.isShowKeyBoard) {
this.keyboardHeight = keyboardHeight; this.keyboardHeight = keyboardHeight;
} }
this.reCalChatMainHeight(); if (this.isShowKeyBoard != isShowKeyBoard) {
this.isShowKeyBoard = isShowKeyBoard;
setTimeout(() => this.reCalChatMainHeight(), 30);
}
}, },
focusInListener() { focusInListener() {
this.isShowKeyBoard = true; this.isShowKeyBoard = true;
this.reCalChatMainHeight(); setTimeout(() => this.reCalChatMainHeight(), 30);
}, },
focusOutListener() { focusOutListener() {
this.isShowKeyBoard = false; this.isShowKeyBoard = false;
this.reCalChatMainHeight(); setTimeout(() => this.reCalChatMainHeight(), 30);
}, },
showBannedTip() { showBannedTip() {
let msgInfo = { let msgInfo = {
@ -1082,7 +1101,11 @@ export default {
// //
this.listenKeyBoard(); this.listenKeyBoard();
// //
this.windowHeight = uni.getSystemInfoSync().windowHeight;
this.screenHeight = uni.getSystemInfoSync().screenHeight;
this.reCalChatMainHeight();
this.$nextTick(() => { this.$nextTick(() => {
// windowHeight
this.windowHeight = uni.getSystemInfoSync().windowHeight; this.windowHeight = uni.getSystemInfoSync().windowHeight;
this.reCalChatMainHeight(); this.reCalChatMainHeight();
this.scrollToBottom(); this.scrollToBottom();
@ -1132,10 +1155,10 @@ export default {
} }
.chat-main-box { .chat-main-box {
// #ifdef H5 // #ifndef APP-PLUS
top: $im-nav-bar-height; top: $im-nav-bar-height;
// #endif // #endif
// #ifndef H5 // #ifdef APP-PLUS
top: calc($im-nav-bar-height + var(--status-bar-height)); top: calc($im-nav-bar-height + var(--status-bar-height));
// #endif // #endif
position: fixed; position: fixed;
@ -1206,7 +1229,7 @@ export default {
border-top: $im-border solid 1px; border-top: $im-border solid 1px;
background-color: $im-bg; background-color: $im-bg;
min-height: 80rpx; min-height: 80rpx;
margin-bottom: 14rpx; padding-bottom: 14rpx;
.iconfont { .iconfont {
font-size: 60rpx; font-size: 60rpx;
@ -1250,13 +1273,14 @@ export default {
background-color: $im-bg; background-color: $im-bg;
.chat-tools { .chat-tools {
display: flex;
flex-wrap: wrap;
align-items: top;
height: 310px;
padding: 40rpx; padding: 40rpx;
box-sizing: border-box; box-sizing: border-box;
.chat-tools-list {
display: flex;
flex-wrap: wrap;
align-content: center;
.chat-tools-item { .chat-tools-item {
width: 25%; width: 25%;
padding: 16rpx; padding: 16rpx;
@ -1284,10 +1308,10 @@ export default {
} }
} }
} }
}
.chat-emotion { .chat-emotion {
height: 310px; padding: 40rpx;
padding: 20rpx;
box-sizing: border-box; box-sizing: border-box;
.emotion-item-list { .emotion-item-list {

10
im-uniapp/pages/friend/friend.vue

@ -12,7 +12,7 @@
温馨提示您现在还没有任何好友快点击右上方'+'按钮添加好友吧~ 温馨提示您现在还没有任何好友快点击右上方'+'按钮添加好友吧~
</view> </view>
<view class="friend-items" v-else> <view class="friend-items" v-else>
<up-index-list :index-list="friendIdx" :sticky="false" :custom-nav-height="50"> <up-index-list :index-list="friendIdx" :sticky="false" :custom-nav-height="customNavHeight">
<template v-for="(friends, i) in friendGroups"> <template v-for="(friends, i) in friendGroups">
<up-index-item> <up-index-item>
<up-index-anchor :text="friendIdx[i] == '*' ? '在线' : friendIdx[i]"></up-index-anchor> <up-index-anchor :text="friendIdx[i] == '*' ? '在线' : friendIdx[i]"></up-index-anchor>
@ -97,6 +97,14 @@ export default {
}, },
hasFriends() { hasFriends() {
return this.friendStore.friends.some(f => !f.deleted); return this.friendStore.friends.some(f => !f.deleted);
},
customNavHeight() {
let h = 50;
// #ifdef APP-PLUS
h += uni.getSystemInfoSync().statusBarHeight;
// #endif
console.log("customNavHeight:",h)
return h;
} }
} }
} }

22
im-uniapp/store/chatStore.js

@ -497,24 +497,28 @@ export default defineStore('chatStore', {
if (!chat) { if (!chat) {
return null; return null;
} }
for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
// 通过id判断 // 通过id判断
if (msgInfo.id && chat.messages[idx].id) { if (msgInfo.id) {
if (msgInfo.id == chat.messages[idx].id) { for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
return chat.messages[idx]; let m = chat.messages[idx];
if (m.id && msgInfo.id == m.id) {
return m;
} }
// 如果id比要查询的消息小,说明没有这条消息 // 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.id > chat.messages[idx].id) { if (m.id && m.id < msgInfo.id) {
break; break;
} }
} }
}
// 正在发送中的消息可能没有id,只有tmpId // 正在发送中的消息可能没有id,只有tmpId
if (msgInfo.tmpId && chat.messages[idx].tmpId) { if (msgInfo.tmpId) {
if (msgInfo.tmpId == chat.messages[idx].tmpId) { for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
return chat.messages[idx]; let m = chat.messages[idx];
if (m.tmpId && msgInfo.tmpId == m.tmpId) {
return m;
} }
// 如果id比要查询的消息小,说明没有这条消息 // 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.tmpId > chat.messages[idx].tmpId) { if (m.tmpId && m.tmpId < msgInfo.tmpId) {
break; break;
} }
} }

44
im-uniapp/store/friendStore.js

@ -5,36 +5,27 @@ import { TERMINAL_TYPE } from '../common/enums.js'
export default defineStore('friendStore', { export default defineStore('friendStore', {
state: () => { state: () => {
return { return {
friends: [], friendMap: new Map(),
timer: null timer: null
} }
}, },
actions: { actions: {
setFriends(friends) { setFriends(friends) {
friends.forEach((f) => { this.friendMap.clear();
f.online = false; friends.forEach(f => this.friendMap.set(f.id, f))
f.onlineWeb = false;
f.onlineApp = false;
})
this.friends = friends;
}, },
updateFriend(friend) { updateFriend(friend) {
let f = this.findFriend(friend.id); let f = this.findFriend(friend.id);
let copy = JSON.parse(JSON.stringify(f)); friend.online = f.online;
Object.assign(f, friend); friend.onlineWeb = f.onlineWeb;
f.online = copy.online; friend.onlineApp = f.onlineApp;
f.onlineWeb = copy.onlineWeb; this.friendMap.set(friend.id, friend)
f.onlineApp = copy.onlineApp;
}, },
removeFriend(id) { removeFriend(id) {
this.friends.filter(f => f.id == id).forEach(f => f.deleted = true); this.friendMap.get(id).deleted = true;
}, },
addFriend(friend) { addFriend(friend) {
if (this.friends.some((f) => f.id == friend.id)) { this.friendMap.set(friend.id, friend)
this.updateFriend(friend)
} else {
this.friends.unshift(friend);
}
}, },
setOnlineStatus(onlineTerminals) { setOnlineStatus(onlineTerminals) {
this.friends.forEach((f) => { this.friends.forEach((f) => {
@ -68,12 +59,11 @@ export default defineStore('friendStore', {
}, 30000) }, 30000)
}, },
setDnd(id, isDnd) { setDnd(id, isDnd) {
let friend = this.findFriend(id); this.friendMap.get(id).isDnd = isDnd;
friend.isDnd = isDnd;
}, },
clear() { clear() {
this.friendMap.clear();
clearTimeout(this.timer); clearTimeout(this.timer);
this.friends = [];
this.timer = null; this.timer = null;
}, },
loadFriend() { loadFriend() {
@ -92,11 +82,15 @@ export default defineStore('friendStore', {
} }
}, },
getters: { getters: {
isFriend: (state) => (userId) => { friends: (state) => {
return state.friends.filter((f) => !f.deleted).some((f) => f.id == userId); return Array.from(state.friendMap.values());
}, },
findFriend: (state) => (id) => { findFriend: (state) => (userId) => {
return state.friends.find((f) => f.id == id); return state.friendMap.get(userId);
},
isFriend: (state) => (userId) => {
let f = state.findFriend(userId)
return f && !f.deleted
} }
} }
}) })

30
im-uniapp/store/groupStore.js

@ -4,33 +4,28 @@ import http from '@/common/request';
export default defineStore('groupStore', { export default defineStore('groupStore', {
state: () => { state: () => {
return { return {
groups: [] groupMap: new Map()
} }
}, },
actions: { actions: {
setGroups(groups) { setGroups(groups) {
this.groups = groups; this.groupMap.clear();
groups.forEach(g => this.groupMap.set(g.id, g))
}, },
addGroup(group) { addGroup(group) {
if (this.groups.some((g) => g.id == group.id)) { this.groupMap.set(group.id, group);
this.updateGroup(group);
} else {
this.groups.unshift(group);
}
}, },
removeGroup(id) { removeGroup(id) {
this.groups.filter(g => g.id == id).forEach(g => g.quit = true); this.groupMap.get(id).quit = true;
}, },
updateGroup(group) { updateGroup(group) {
let g = this.findGroup(group.id); this.groupMap.set(group.id, group);
Object.assign(g, group);
}, },
setDnd(id, isDnd) { setDnd(id, isDnd) {
let group = this.findGroup(id); this.groupMap.get(id).isDnd = isDnd;
group.isDnd = isDnd;
}, },
clear() { clear() {
this.groups = []; this.groupMap.clear();
}, },
loadGroup() { loadGroup() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -47,8 +42,15 @@ export default defineStore('groupStore', {
} }
}, },
getters: { getters: {
groups: (state) => {
return Array.from(state.groupMap.values());
},
findGroup: (state) => (id) => { findGroup: (state) => (id) => {
return state.groups.find((g) => g.id == id); return state.groupMap.get(id);
},
isGroup: (state) => (id) => {
let group = state.findGroup(id);
return group && !group.quit
} }
} }
}) })

22
im-web/src/components/chat/ChatBox.vue

@ -163,13 +163,13 @@ export default {
msgInfo.id = m.id; msgInfo.id = m.id;
msgInfo.status = m.status; msgInfo.status = m.status;
this.isReceipt = false; this.isReceipt = false;
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}) })
}, },
onImageFail(e, file) { onImageFail(e, file) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}, },
onImageBefore(file) { onImageBefore(file) {
// //
@ -208,7 +208,7 @@ export default {
data.width = size.width; data.width = size.width;
data.height = size.height; data.height = size.height;
msgInfo.content = JSON.stringify(data) msgInfo.content = JSON.stringify(data)
this.chatStore.insertMessage(msgInfo, chat); this.chatStore.updateMessage(msgInfo, chat);
this.scrollToBottom(); this.scrollToBottom();
}) })
}, },
@ -226,13 +226,13 @@ export default {
msgInfo.status = m.status; msgInfo.status = m.status;
this.isReceipt = false; this.isReceipt = false;
this.refreshPlaceHolder(); this.refreshPlaceHolder();
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}) })
}, },
onFileFail(e, file) { onFileFail(e, file) {
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo)); let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED; msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(msgInfo, file.chat); this.chatStore.updateMessage(msgInfo, file.chat);
}, },
onFileBefore(file) { onFileBefore(file) {
// //
@ -384,7 +384,7 @@ export default {
// //
tmpMessage.id = m.id; tmpMessage.id = m.id;
tmpMessage.status = m.status; tmpMessage.status = m.status;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
// //
this.moveChatToTop(); this.moveChatToTop();
// //
@ -397,7 +397,7 @@ export default {
this.refreshPlaceHolder(); this.refreshPlaceHolder();
}).catch(() => { }).catch(() => {
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED; tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(tmpMessage, this.chat); this.chatStore.updateMessage(tmpMessage, this.chat);
}) })
}, },
fillTargetId(msgInfo, targetId) { fillTargetId(msgInfo, targetId) {
@ -483,11 +483,11 @@ export default {
tmpMessage.id = m.id; tmpMessage.id = m.id;
tmpMessage.status = m.status; tmpMessage.status = m.status;
tmpMessage.content = m.content; tmpMessage.content = m.content;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}).catch(() => { }).catch(() => {
// //
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED; tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}).finally(() => { }).finally(() => {
this.isReceipt = false; this.isReceipt = false;
resolve(); resolve();
@ -522,11 +522,11 @@ export default {
tmpMessage.id = m.id; tmpMessage.id = m.id;
tmpMessage.status = m.status; tmpMessage.status = m.status;
tmpMessage.content = m.content; tmpMessage.content = m.content;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}).catch(() => { }).catch(() => {
// //
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED; tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.insertMessage(tmpMessage, chat); this.chatStore.updateMessage(tmpMessage, chat);
}).finally(() => { }).finally(() => {
this.scrollToBottom(); this.scrollToBottom();
}); });

3
im-web/src/components/chat/ChatMessageItem.vue

@ -23,11 +23,8 @@
<span class="message-text" v-if="isTextMessage" v-html="htmlText"></span> <span class="message-text" v-if="isTextMessage" v-html="htmlText"></span>
<div class="message-image" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE" <div class="message-image" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE"
@click="showFullImageBox()"> @click="showFullImageBox()">
<div v-loading="sending" element-loading-text="发送中.."
element-loading-background="rgba(0, 0, 0, 0.4)">
<img :style="imageStyle" :src="contentData.thumbUrl" loading="lazy" /> <img :style="imageStyle" :src="contentData.thumbUrl" loading="lazy" />
</div> </div>
</div>
<div class="message-file" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.FILE"> <div class="message-file" v-else-if="msgInfo.type == $enums.MESSAGE_TYPE.FILE">
<div class="chat-file-box" v-loading="sending"> <div class="chat-file-box" v-loading="sending">
<div class="chat-file-info"> <div class="chat-file-info">

24
im-web/src/store/chatStore.js

@ -466,7 +466,8 @@ export default defineStore('chatStore', {
// 冷热消息合并 // 冷热消息合并
let chat = Object.assign({}, coldChat, hotChat); let chat = Object.assign({}, coldChat, hotChat);
if (hotChat && coldChat) { if (hotChat && coldChat) {
chat.messages = coldChat.messages.concat(hotChat.messages) chat.messages = coldChat.messages.concat(hotChat
.messages)
} }
// 历史版本没有readedMessageIdx字段,做兼容一下 // 历史版本没有readedMessageIdx字段,做兼容一下
chat.readedMessageIdx = chat.readedMessageIdx || 0; chat.readedMessageIdx = chat.readedMessageIdx || 0;
@ -522,24 +523,27 @@ export default defineStore('chatStore', {
if (!chat) { if (!chat) {
return null; return null;
} }
if (msgInfo.id) {
for (let idx = chat.messages.length - 1; idx >= 0; idx--) { for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
// 通过id判断 let m = chat.messages[idx];
if (msgInfo.id && chat.messages[idx].id) { if (m.id && msgInfo.id == m.id) {
if (msgInfo.id == chat.messages[idx].id) { return m;
return chat.messages[idx];
} }
// 如果id比要查询的消息小,说明没有这条消息 // 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.id > chat.messages[idx].id) { if (m.id && m.id < msgInfo.id) {
break; break;
} }
} }
}
// 正在发送中的消息可能没有id,只有tmpId // 正在发送中的消息可能没有id,只有tmpId
if (msgInfo.tmpId && chat.messages[idx].tmpId) { if (msgInfo.tmpId) {
if (msgInfo.tmpId == chat.messages[idx].tmpId) { for (let idx = chat.messages.length - 1; idx >= 0; idx--) {
return chat.messages[idx]; let m = chat.messages[idx];
if (m.tmpId && msgInfo.tmpId == m.tmpId) {
return m;
} }
// 如果id比要查询的消息小,说明没有这条消息 // 如果id比要查询的消息小,说明没有这条消息
if (msgInfo.tmpId > chat.messages[idx].tmpId) { if (m.tmpId && m.tmpId < msgInfo.tmpId) {
break; break;
} }
} }

BIN
截图/app/1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
截图/app/2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
截图/web/单人通话.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

After

Width:  |  Height:  |  Size: 168 KiB

BIN
截图/web/多人通话.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 202 KiB

BIN
截图/web/好友.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 141 KiB

BIN
截图/web/私聊.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

After

Width:  |  Height:  |  Size: 144 KiB

BIN
截图/web/群列表.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 KiB

After

Width:  |  Height:  |  Size: 189 KiB

BIN
截图/web/群聊.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 187 KiB

BIN
截图/消息推送集群化.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Loading…
Cancel
Save