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", "backgroundColor": "#ffffff",
"list": [{ "list": [{
"pagePath": "pages/chat/chat", "pagePath": "pages/chat/chat",
"iconPath": "static/tarbar/chat.png", "iconPath": "static/tab/chat.png",
"selectedIconPath": "static/tarbar/chat_active.png", "selectedIconPath": "static/tab/chat_active.png",
"text": "消息" "text": "消息"
}, },
{ {
"pagePath": "pages/friend/friend", "pagePath": "pages/friend/friend",
"iconPath": "static/tarbar/friend.png", "iconPath": "static/tab/friend.png",
"selectedIconPath": "static/tarbar/friend_active.png", "selectedIconPath": "static/tab/friend_active.png",
"text": "好友" "text": "好友"
}, },
{ {
"pagePath": "pages/group/group", "pagePath": "pages/group/group",
"iconPath": "static/tarbar/group.png", "iconPath": "static/tab/group.png",
"selectedIconPath": "static/tarbar/group_active.png", "selectedIconPath": "static/tab/group_active.png",
"text": "群聊" "text": "群聊"
}, },
{ {
"pagePath": "pages/mine/mine", "pagePath": "pages/mine/mine",
"iconPath": "static/tarbar/mine.png", "iconPath": "static/tab/mine.png",
"selectedIconPath": "static/tarbar/mine_active.png", "selectedIconPath": "static/tab/mine_active.png",
"text": "我的" "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; return;
} }
let sendText = this.isReceipt ? "【回执消息】" : ""; let sendText = this.isReceipt ? "【回执消息】" : "";
let promiseList = []; fullList.forEach(async msg => {
for (let i = 0; i < fullList.length; i++) {
let msg = fullList[i];
switch (msg.type) { switch (msg.type) {
case "text": case "text":
await this.sendTextMessage(sendText + msg.content, msg.atUserIds); await this.sendTextMessage(sendText + msg.content, msg.atUserIds);
@ -413,8 +411,7 @@ export default {
await this.sendFileMessage(msg.content.file); await this.sendFileMessage(msg.content.file);
break; break;
} }
})
}
}, },
sendImageMessage(file) { sendImageMessage(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

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

@ -1,8 +1,9 @@
<template> <template>
<div class="chat-input-area"> <div class="chat-input-area">
<div :class="['edit-chat-container', isEmpty ? '' : 'not-empty']" contenteditable="true" @paste.prevent="onPaste" <div :class="['edit-chat-container', isEmpty ? '' : 'not-empty']" contenteditable="true"
@keydown="onKeydown" @compositionstart="compositionFlag = true" @compositionend="onCompositionEnd" @paste.prevent="onPaste" @keydown="onKeydown" @compositionstart="compositionFlag = true"
@input="onEditorInput" @mousedown="onMousedown" ref="content" @blur="onBlur"> @compositionend="onCompositionEnd" @input="onEditorInput" @mousedown="onMousedown" ref="content"
@blur="onBlur">
</div> </div>
<chat-at-box @select="onAtSelect" :search-text="atSearchText" ref="atBox" :ownerId="ownerId" <chat-at-box @select="onAtSelect" :search-text="atSearchText" ref="atBox" :ownerId="ownerId"
:members="groupMembers"></chat-at-box> :members="groupMembers"></chat-at-box>
@ -45,7 +46,7 @@ export default {
range.deleteContents(); range.deleteContents();
} }
// //
if (txt && typeof (txt) == 'string') { if (txt && typeof(txt) == 'string') {
let textNode = document.createTextNode(txt); let textNode = document.createTextNode(txt);
range.insertNode(textNode) range.insertNode(textNode)
range.collapse(); range.collapse();
@ -234,7 +235,7 @@ export default {
}, },
onBlur(e) { onBlur(e) {
if(!this.atIng){ if (!this.atIng) {
this.updateRange(); this.updateRange();
} }
}, },
@ -342,6 +343,7 @@ export default {
this.empty(); this.empty();
this.imageList = []; this.imageList = [];
this.fileList = []; this.fileList = [];
this.$refs.atBox.close();
}, },
empty() { empty() {
this.$refs.content.innerHTML = ""; this.$refs.content.innerHTML = "";
@ -366,13 +368,13 @@ export default {
this.updateRange(); this.updateRange();
}, },
html2Escape(strHtml) { html2Escape(strHtml) {
return strHtml.replace(/[<>&"]/g, function (c) { return strHtml.replace(/[<>&"]/g, function(c) {
return { return {
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',
'&': '&amp;', '&': '&amp;',
'"': '&quot;' '"': '&quot;'
}[c]; } [c];
}); });
}, },
submit() { submit() {
@ -440,7 +442,7 @@ export default {
if (node.dataset.id) { if (node.dataset.id) {
tempText += node.innerHTML; tempText += node.innerHTML;
atUserIds.push(node.dataset.id) atUserIds.push(node.dataset.id)
} else if(node.outerHtml) { } else if (node.outerHtml) {
tempText += 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">
<div class="chat-name-text"> <div class="chat-name-text">
<div>{{ chat.showName }}</div> <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>
<div class="chat-time-text">{{ showTime }}</div> <div class="chat-time-text">{{ showTime }}</div>
</div> </div>
@ -53,9 +53,6 @@ export default {
}, },
active: { active: {
type: Boolean type: Boolean
},
index: {
type: Number
} }
}, },
methods: { methods: {
@ -159,15 +156,9 @@ export default {
.el-tag { .el-tag {
min-width: 22px; min-width: 22px;
text-align: center; text-align: center;
background-color: #2830d3;
border-radius: 10px; border-radius: 10px;
border: 0; border: 0;
height: 16px; 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> <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 v-show="mode == 'RECORD'">
<div class="tip">{{ stateTip }}</div> <div class="tip">{{ stateTip }}</div>
<div>时长: {{ state == 'STOP' ? 0 : parseInt(rc.duration) }}s</div> <div>时长: {{ state == 'STOP' ? 0 : parseInt(rc.duration) }}s</div>

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

@ -1,5 +1,5 @@
<template> <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"> custom-class="add-friend-dialog">
<el-input placeholder="输入用户名或昵称按下enter搜索,最多展示20条" class="input-with-select" v-model="searchText" size="small" <el-input placeholder="输入用户名或昵称按下enter搜索,最多展示20条" class="input-with-select" v-model="searchText" size="small"
@keyup.enter.native="onSearch()"> @keyup.enter.native="onSearch()">

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

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

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

@ -1,5 +1,5 @@
<template> <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="group-member-selector">
<div class="left-box"> <div class="left-box">
<el-input placeholder="搜索" v-model="searchText"> <el-input placeholder="搜索" v-model="searchText">

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

@ -1,5 +1,5 @@
<template> <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="rtc-group-join">
<div class="host-info"> <div class="host-info">
<head-image :name="rtcInfo.host.nickName" :url="rtcInfo.host.headImage" :size="80"></head-image> <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> <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 :model="userInfo" label-width="80px" :rules="rules" ref="settingForm" size="small">
<el-form-item label="头像" style="margin-bottom: 0 !important;"> <el-form-item label="头像" style="margin-bottom: 0 !important;">
<file-upload class="avatar-uploader" :action="imageAction" :showLoading="true" :maxSize="maxSize" <file-upload class="avatar-uploader" :action="imageAction" :showLoading="true" :maxSize="maxSize"

Loading…
Cancel
Save