Browse Source

1.弹出框支持拖拽

2.移动端tab按钮样式优化
3.修复web端发送多个文件的bug
master
xsx 11 months ago
parent
commit
164cad0b59
  1. 16
      im-uniapp/pages.json
  2. 0
      im-uniapp/static/tab/chat.png
  3. BIN
      im-uniapp/static/tab/chat_active.png
  4. 0
      im-uniapp/static/tab/friend.png
  5. BIN
      im-uniapp/static/tab/friend_active.png
  6. 0
      im-uniapp/static/tab/group.png
  7. BIN
      im-uniapp/static/tab/group_active.png
  8. BIN
      im-uniapp/static/tab/mine.png
  9. BIN
      im-uniapp/static/tab/mine_active.png
  10. BIN
      im-uniapp/static/tarbar/chat_active.png
  11. BIN
      im-uniapp/static/tarbar/friend_active.png
  12. BIN
      im-uniapp/static/tarbar/group_active.png
  13. BIN
      im-uniapp/static/tarbar/mine.png
  14. BIN
      im-uniapp/static/tarbar/mine_active.png
  15. 7
      im-web/src/components/chat/ChatBox.vue
  16. 20
      im-web/src/components/chat/ChatInput.vue
  17. 11
      im-web/src/components/chat/ChatItem.vue
  18. 2
      im-web/src/components/chat/ChatRecord.vue
  19. 2
      im-web/src/components/friend/AddFriend.vue
  20. 311
      im-web/src/components/group/AddGroupMember.vue
  21. 2
      im-web/src/components/group/GroupMemberSelector.vue
  22. 2
      im-web/src/components/rtc/RtcGroupJoin.vue
  23. 2
      im-web/src/components/setting/Setting.vue

16
im-uniapp/pages.json

@ -78,26 +78,26 @@
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/chat/chat",
"iconPath": "static/tarbar/chat.png",
"selectedIconPath": "static/tarbar/chat_active.png",
"iconPath": "static/tab/chat.png",
"selectedIconPath": "static/tab/chat_active.png",
"text": "消息"
},
{
"pagePath": "pages/friend/friend",
"iconPath": "static/tarbar/friend.png",
"selectedIconPath": "static/tarbar/friend_active.png",
"iconPath": "static/tab/friend.png",
"selectedIconPath": "static/tab/friend_active.png",
"text": "好友"
},
{
"pagePath": "pages/group/group",
"iconPath": "static/tarbar/group.png",
"selectedIconPath": "static/tarbar/group_active.png",
"iconPath": "static/tab/group.png",
"selectedIconPath": "static/tab/group_active.png",
"text": "群聊"
},
{
"pagePath": "pages/mine/mine",
"iconPath": "static/tarbar/mine.png",
"selectedIconPath": "static/tarbar/mine_active.png",
"iconPath": "static/tab/mine.png",
"selectedIconPath": "static/tab/mine_active.png",
"text": "我的"
}
]

0
im-uniapp/static/tarbar/chat.png → im-uniapp/static/tab/chat.png

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
im-uniapp/static/tab/chat_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

0
im-uniapp/static/tarbar/friend.png → im-uniapp/static/tab/friend.png

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
im-uniapp/static/tab/friend_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

0
im-uniapp/static/tarbar/group.png → im-uniapp/static/tab/group.png

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
im-uniapp/static/tab/group_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
im-uniapp/static/tab/mine.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
im-uniapp/static/tab/mine_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
im-uniapp/static/tarbar/chat_active.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

BIN
im-uniapp/static/tarbar/friend_active.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

BIN
im-uniapp/static/tarbar/group_active.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

BIN
im-uniapp/static/tarbar/mine.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

BIN
im-uniapp/static/tarbar/mine_active.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

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

@ -399,9 +399,7 @@ export default {
return;
}
let sendText = this.isReceipt ? "【回执消息】" : "";
let promiseList = [];
for (let i = 0; i < fullList.length; i++) {
let msg = fullList[i];
fullList.forEach(async msg => {
switch (msg.type) {
case "text":
await this.sendTextMessage(sendText + msg.content, msg.atUserIds);
@ -413,8 +411,7 @@ export default {
await this.sendFileMessage(msg.content.file);
break;
}
}
})
},
sendImageMessage(file) {
return new Promise((resolve, reject) => {

20
im-web/src/components/chat/ChatInput.vue

@ -1,8 +1,9 @@
<template>
<div class="chat-input-area">
<div :class="['edit-chat-container', isEmpty ? '' : 'not-empty']" contenteditable="true" @paste.prevent="onPaste"
@keydown="onKeydown" @compositionstart="compositionFlag = true" @compositionend="onCompositionEnd"
@input="onEditorInput" @mousedown="onMousedown" ref="content" @blur="onBlur">
<div :class="['edit-chat-container', isEmpty ? '' : 'not-empty']" contenteditable="true"
@paste.prevent="onPaste" @keydown="onKeydown" @compositionstart="compositionFlag = true"
@compositionend="onCompositionEnd" @input="onEditorInput" @mousedown="onMousedown" ref="content"
@blur="onBlur">
</div>
<chat-at-box @select="onAtSelect" :search-text="atSearchText" ref="atBox" :ownerId="ownerId"
:members="groupMembers"></chat-at-box>
@ -45,7 +46,7 @@ export default {
range.deleteContents();
}
//
if (txt && typeof (txt) == 'string') {
if (txt && typeof(txt) == 'string') {
let textNode = document.createTextNode(txt);
range.insertNode(textNode)
range.collapse();
@ -234,7 +235,7 @@ export default {
},
onBlur(e) {
if(!this.atIng){
if (!this.atIng) {
this.updateRange();
}
},
@ -342,6 +343,7 @@ export default {
this.empty();
this.imageList = [];
this.fileList = [];
this.$refs.atBox.close();
},
empty() {
this.$refs.content.innerHTML = "";
@ -366,13 +368,13 @@ export default {
this.updateRange();
},
html2Escape(strHtml) {
return strHtml.replace(/[<>&"]/g, function (c) {
return strHtml.replace(/[<>&"]/g, function(c) {
return {
'<': '&lt;',
'>': '&gt;',
'&': '&amp;',
'"': '&quot;'
}[c];
} [c];
});
},
submit() {
@ -440,7 +442,7 @@ export default {
if (node.dataset.id) {
tempText += node.innerHTML;
atUserIds.push(node.dataset.id)
} else if(node.outerHtml) {
} else if (node.outerHtml) {
tempText += node.outerHtml;
}
}
@ -554,4 +556,4 @@ export default {
}
}
</style>
</style>

11
im-web/src/components/chat/ChatItem.vue

@ -9,7 +9,7 @@
<div class="chat-name">
<div class="chat-name-text">
<div>{{ chat.showName }}</div>
<el-tag v-if="chat.type == 'GROUP'" size="mini" effect="dark"></el-tag>
<el-tag v-if="chat.type == 'GROUP'" size="mini" ></el-tag>
</div>
<div class="chat-time-text">{{ showTime }}</div>
</div>
@ -53,9 +53,6 @@ export default {
},
active: {
type: Boolean
},
index: {
type: Number
}
},
methods: {
@ -159,15 +156,9 @@ export default {
.el-tag {
min-width: 22px;
text-align: center;
background-color: #2830d3;
border-radius: 10px;
border: 0;
height: 16px;
line-height: 16px;
font-size: 10px;
margin-left: 2px;
opacity: 0.8;
}
}

2
im-web/src/components/chat/ChatRecord.vue

@ -1,5 +1,5 @@
<template>
<el-dialog class="chat-record" title="语音录制" :visible.sync="visible" width="600px" :before-close="onClose">
<el-dialog v-dialogDrag class="chat-record" title="语音录制" :visible.sync="visible" width="600px" :before-close="onClose">
<div v-show="mode == 'RECORD'">
<div class="tip">{{ stateTip }}</div>
<div>时长: {{ state == 'STOP' ? 0 : parseInt(rc.duration) }}s</div>

2
im-web/src/components/friend/AddFriend.vue

@ -1,5 +1,5 @@
<template>
<el-dialog title="添加好友" :visible.sync="dialogVisible" width="400px" :before-close="onClose"
<el-dialog v-dialogDrag title="添加好友" :visible.sync="dialogVisible" width="400px" :before-close="onClose"
custom-class="add-friend-dialog">
<el-input placeholder="输入用户名或昵称按下enter搜索,最多展示20条" class="input-with-select" v-model="searchText" size="small"
@keyup.enter.native="onSearch()">

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

@ -1,174 +1,173 @@
<template>
<el-dialog title="邀请好友" :visible.sync="show" width="620px" :before-close="close">
<div class="agm-container">
<div class="agm-l-box">
<div class="search">
<el-input placeholder="搜索好友" v-model="searchText" size="small">
<i class="el-icon-search el-input__icon" slot="suffix"> </i>
</el-input>
</div>
<el-scrollbar style="height:400px;">
<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" :active="false">
<el-checkbox :disabled="friend.disabled" @click.native.stop="" class="agm-friend-checkbox"
v-model="friend.isCheck" size="medium"></el-checkbox>
</friend-item>
</div>
</el-scrollbar>
</div>
<div class="agm-arrow el-icon-d-arrow-right"></div>
<div class="agm-r-box">
<div class="agm-select-tip"> 已勾选{{ checkCount }}位好友</div>
<el-scrollbar style="height:400px;">
<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>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="close()"> </el-button>
<el-button type="primary" @click="onOk()"> </el-button>
</span>
</el-dialog>
<el-dialog v-dialogDrag title="邀请好友" :visible.sync="show" width="620px" :before-close="close">
<div class="add-group-member">
<div class="left-box">
<div class="search">
<el-input placeholder="搜索好友" v-model="searchText" size="small">
<i class="el-icon-search el-input__icon" slot="suffix"> </i>
</el-input>
</div>
<el-scrollbar style="height:400px;">
<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" :active="false">
<el-checkbox :disabled="friend.disabled" @click.native.stop="" class="friend-checkbox"
v-model="friend.isCheck" size="medium"></el-checkbox>
</friend-item>
</div>
</el-scrollbar>
</div>
<div class="arrow el-icon-d-arrow-right"></div>
<div class="right-box">
<div class="tip"> 已勾选{{ checkCount }}位好友</div>
<el-scrollbar style="height:400px;">
<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>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="close()"> </el-button>
<el-button type="primary" @click="onOk()"> </el-button>
</span>
</el-dialog>
</template>
<script>
import FriendItem from '../friend/FriendItem.vue';
export default {
name: "addGroupMember",
components: {
FriendItem
},
data() {
return {
show: false,
searchText: "",
friends: []
}
},
methods: {
open() {
this.show = true;
this.friends = [];
this.$store.state.friendStore.friends.forEach((f) => {
if (f.deleted) {
return;
}
let friend = JSON.parse(JSON.stringify(f))
let m = this.members.filter((m) => !m.quit)
.find((m) => m.userId == f.id);
if (m) {
//
friend.disabled = true;
friend.isCheck = true
} else {
friend.disabled = false;
friend.isCheck = false;
}
this.friends.push(friend);
})
},
close() {
this.show = false;
},
onOk() {
let inviteVO = {
groupId: this.groupId,
friendIds: []
}
this.friends.forEach((f) => {
if (f.isCheck && !f.disabled) {
inviteVO.friendIds.push(f.id);
}
})
if (inviteVO.friendIds.length > 0) {
this.$http({
url: "/group/invite",
method: 'post',
data: inviteVO
}).then(() => {
this.$message.success("邀请成功");
this.$emit("reload");
this.close()
})
}
},
onRemoveFriend(friend) {
friend.isCheck = false;
},
onSwitchCheck(friend) {
if (!friend.disabled) {
friend.isCheck = !friend.isCheck
}
}
},
props: {
groupId: {
type: Number
},
members: {
type: Array
}
},
computed: {
checkCount() {
return this.friends.filter((f) => f.isCheck && !f.disabled).length;
}
}
name: "addGroupMember",
components: {
FriendItem
},
data() {
return {
show: false,
searchText: "",
friends: []
}
},
methods: {
open() {
this.show = true;
this.friends = [];
this.$store.state.friendStore.friends.forEach((f) => {
if (f.deleted) {
return;
}
let friend = JSON.parse(JSON.stringify(f))
let m = this.members.filter((m) => !m.quit)
.find((m) => m.userId == f.id);
if (m) {
//
friend.disabled = true;
friend.isCheck = true
} else {
friend.disabled = false;
friend.isCheck = false;
}
this.friends.push(friend);
})
},
close() {
this.show = false;
},
onOk() {
let inviteVO = {
groupId: this.groupId,
friendIds: []
}
this.friends.forEach((f) => {
if (f.isCheck && !f.disabled) {
inviteVO.friendIds.push(f.id);
}
})
if (inviteVO.friendIds.length > 0) {
this.$http({
url: "/group/invite",
method: 'post',
data: inviteVO
}).then(() => {
this.$message.success("邀请成功");
this.$emit("reload");
this.close()
})
}
},
onRemoveFriend(friend) {
friend.isCheck = false;
},
onSwitchCheck(friend) {
if (!friend.disabled) {
friend.isCheck = !friend.isCheck
}
}
},
props: {
groupId: {
type: Number
},
members: {
type: Array
}
},
computed: {
checkCount() {
return this.friends.filter((f) => f.isCheck && !f.disabled).length;
}
}
}
</script>
<style lang="scss">
.agm-container {
display: flex;
<style lang="scss" scoped>
.add-group-member {
display: flex;
.agm-l-box {
flex: 1;
overflow: hidden;
border: var(--im-border);
.left-box {
flex: 1;
overflow: hidden;
border: var(--im-border);
.search {
height: 40px;
display: flex;
align-items: center;
.search {
height: 40px;
display: flex;
align-items: center;
.el-input__inner {
border: unset;
border-bottom: var(--im-border);
}
.el-input__inner {
border: unset;
border-bottom: var(--im-border);
}
}
}
.friend-checkbox {
margin-right: 20px;
}
}
.agm-friend-checkbox {
margin-right: 20px;
}
}
.arrow {
display: flex;
align-items: center;
font-size: 18px;
padding: 10px;
font-weight: 600;
color: var(--im-color-primary);
}
.agm-arrow {
display: flex;
align-items: center;
font-size: 18px;
padding: 10px;
font-weight: 600;
color: var(--im-color-primary);
}
.right-box {
flex: 1;
border: var(--im-border);
.agm-r-box {
flex: 1;
border: var(--im-border);
.agm-select-tip {
text-align: left;
height: 40px;
line-height: 40px;
text-indent: 6px;
color: var(--im-text-color-light)
}
}
.tip {
text-align: left;
height: 40px;
line-height: 40px;
text-indent: 10px;
color: var(--im-text-color-light)
}
}
}
</style>
</style>

2
im-web/src/components/group/GroupMemberSelector.vue

@ -1,5 +1,5 @@
<template>
<el-dialog :title="title" :visible.sync="isShow" width="700px">
<el-dialog v-dialogDrag :title="title" :visible.sync="isShow" width="700px">
<div class="group-member-selector">
<div class="left-box">
<el-input placeholder="搜索" v-model="searchText">

2
im-web/src/components/rtc/RtcGroupJoin.vue

@ -1,5 +1,5 @@
<template>
<el-dialog title="是否加入通话?" :visible.sync="isShow" width="400px">
<el-dialog v-dialogDrag title="是否加入通话?" :visible.sync="isShow" width="400px">
<div class="rtc-group-join">
<div class="host-info">
<head-image :name="rtcInfo.host.nickName" :url="rtcInfo.host.headImage" :size="80"></head-image>

2
im-web/src/components/setting/Setting.vue

@ -1,5 +1,5 @@
<template>
<el-dialog class="setting" title="设置" :visible.sync="visible" width="420px" :before-close="onClose">
<el-dialog v-dialogDrag class="setting" title="设置" :visible.sync="visible" width="420px" :before-close="onClose">
<el-form :model="userInfo" label-width="80px" :rules="rules" ref="settingForm" size="small">
<el-form-item label="头像" style="margin-bottom: 0 !important;">
<file-upload class="avatar-uploader" :action="imageAction" :showLoading="true" :maxSize="maxSize"

Loading…
Cancel
Save