37 changed files with 2153 additions and 1555 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
@ -0,0 +1,67 @@ |
|||||
|
<template> |
||||
|
<div class="chat-group-member" :style="{'height':height+'px'}"> |
||||
|
<div class="member-avatar"> |
||||
|
<head-image :size="headImageSize" :name="member.aliasName" :url="member.headImage"> </head-image> |
||||
|
</div> |
||||
|
<div class="member-name" :style="{'line-height':height+'px'}"> |
||||
|
<div>{{ member.aliasName }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HeadImage from "../common/HeadImage.vue"; |
||||
|
export default { |
||||
|
name: "groupMember", |
||||
|
components: { HeadImage }, |
||||
|
data() { |
||||
|
return {}; |
||||
|
}, |
||||
|
props: { |
||||
|
member: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
}, |
||||
|
height:{ |
||||
|
type: Number, |
||||
|
default: 50 |
||||
|
} |
||||
|
}, |
||||
|
computed:{ |
||||
|
headImageSize(){ |
||||
|
return Math.ceil(this.height * 0.75) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.chat-group-member { |
||||
|
display: flex; |
||||
|
margin-bottom: 1px; |
||||
|
position: relative; |
||||
|
padding: 0 5px; |
||||
|
align-items: center; |
||||
|
background-color: #fafafa; |
||||
|
white-space: nowrap; |
||||
|
box-sizing: border-box; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: #eeeeee; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
background-color: #eeeeee; |
||||
|
} |
||||
|
|
||||
|
.member-name { |
||||
|
padding-left: 10px; |
||||
|
height: 100%; |
||||
|
text-align: left; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
font-size: 14px; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,186 @@ |
|||||
|
<template> |
||||
|
<div v-show="show"> |
||||
|
<div class="chat-group-readed-mask" @click.self="close()"> |
||||
|
<div class="chat-group-readed" :style="{ 'left': pos.x + 'px', 'top': pos.y + 'px' }" @click.prevent=""> |
||||
|
<el-tabs type="border-card" :stretch="true"> |
||||
|
<el-tab-pane :label="`已读(${readedMembers.length})`"> |
||||
|
<el-scrollbar class="scroll-box"> |
||||
|
<div v-for="(member) in readedMembers" :key="member.id"> |
||||
|
<chat-group-member :member="member"></chat-group-member> |
||||
|
</div> |
||||
|
</el-scrollbar> |
||||
|
</el-tab-pane> |
||||
|
<el-tab-pane :label="`未读(${unreadMembers.length})`"> |
||||
|
<el-scrollbar class="scroll-box"> |
||||
|
<div v-for="(member) in unreadMembers" :key="member.id"> |
||||
|
<chat-group-member :member="member"></chat-group-member> |
||||
|
</div> |
||||
|
</el-scrollbar> |
||||
|
</el-tab-pane> |
||||
|
</el-tabs> |
||||
|
<div v-show="msgInfo.selfSend" class="arrow-right" :style="{ 'top': pos.arrowY + 'px' }"> |
||||
|
<div class="arrow-right-inner"> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div v-show="!msgInfo.selfSend" class="arrow-left" :style="{ 'top': pos.arrowY + 'px' }"> |
||||
|
<div class="arrow-left-inner"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
|
||||
|
<script> |
||||
|
import ChatGroupMember from "./ChatGroupMember.vue"; |
||||
|
|
||||
|
export default { |
||||
|
name: "chatGroupReaded", |
||||
|
components: { |
||||
|
ChatGroupMember |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
show: false, |
||||
|
pos: { |
||||
|
x: 0, |
||||
|
y: 0, |
||||
|
arrowY: 0 |
||||
|
}, |
||||
|
msgInfo: {}, |
||||
|
readedMembers: [], |
||||
|
unreadMembers: [] |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
groupMembers: { |
||||
|
type: Array |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
close() { |
||||
|
this.show = false; |
||||
|
}, |
||||
|
open(msgInfo, rect) { |
||||
|
this.show = true; |
||||
|
this.msgInfo = msgInfo; |
||||
|
this.pos.arrowY = 200; |
||||
|
// 计算窗口位置 |
||||
|
if (this.msgInfo.selfSend) { |
||||
|
// 自己发的消息弹出在消息的左边 |
||||
|
this.pos.x = rect.left - 310; |
||||
|
} else { |
||||
|
// 别人发的消息弹窗在消息右边 |
||||
|
this.pos.x = rect.right + 20; |
||||
|
} |
||||
|
this.pos.y = rect.top + rect.height / 2 - 215; |
||||
|
// 防止窗口溢出 |
||||
|
if (this.pos.y < 0) { |
||||
|
this.pos.arrowY += this.pos.y |
||||
|
this.pos.y = 0; |
||||
|
} |
||||
|
this.loadReadedUser() |
||||
|
}, |
||||
|
loadReadedUser() { |
||||
|
this.readedMembers = []; |
||||
|
this.unreadMembers = []; |
||||
|
this.$http({ |
||||
|
url: "/message/group/findReadedUsers", |
||||
|
method: 'get', |
||||
|
params: { groupId: this.msgInfo.groupId, messageId: this.msgInfo.id } |
||||
|
}).then(userIds => { |
||||
|
this.groupMembers.forEach(member => { |
||||
|
// 发送者和已退群的不显示 |
||||
|
if (member.userId == this.msgInfo.sendId && member.quit) { |
||||
|
return; |
||||
|
} |
||||
|
// 区分已读还是未读 |
||||
|
if (userIds.find(userId => member.userId == userId)) { |
||||
|
this.readedMembers.push(member); |
||||
|
} else { |
||||
|
this.unreadMembers.push(member); |
||||
|
} |
||||
|
}) |
||||
|
// 更新已读人数 |
||||
|
this.$store.commit("updateMessage", { |
||||
|
id: this.msgInfo.id, |
||||
|
groupId: this.msgInfo.groupId, |
||||
|
readedCount: this.readedMembers.length |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.chat-group-readed-mask { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
z-index: 9999; |
||||
|
} |
||||
|
|
||||
|
.chat-group-readed { |
||||
|
position: fixed; |
||||
|
box-shadow: 0px 0px 10px #ccc; |
||||
|
width: 300px; |
||||
|
background-color: #fafafa; |
||||
|
border-radius: 8px; |
||||
|
|
||||
|
.scroll-box { |
||||
|
height: 400px; |
||||
|
} |
||||
|
|
||||
|
.arrow-left { |
||||
|
|
||||
|
position: absolute; |
||||
|
left: -15px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-top: 15px solid transparent; |
||||
|
border-bottom: 15px solid transparent; |
||||
|
border-right: 15px solid #ccc; |
||||
|
|
||||
|
.arrow-left-inner { |
||||
|
position: absolute; |
||||
|
top: -12px; |
||||
|
left: 3px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
overflow: hidden; |
||||
|
border-top: 12px solid transparent; |
||||
|
border-bottom: 12px solid transparent; |
||||
|
border-right: 12px solid white; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.arrow-right { |
||||
|
position: absolute; |
||||
|
right: -15px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-top: 15px solid transparent; |
||||
|
border-bottom: 15px solid transparent; |
||||
|
border-left: 15px solid #ccc; |
||||
|
|
||||
|
.arrow-right-inner { |
||||
|
position: absolute; |
||||
|
top: -12px; |
||||
|
right: 3px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
overflow: hidden; |
||||
|
border-top: 12px solid transparent; |
||||
|
border-bottom: 12px solid transparent; |
||||
|
border-left: 12px solid white; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -1,408 +1,433 @@ |
|||||
<template> |
<template> |
||||
<div class="chat-msg-item"> |
<div class="chat-msg-item"> |
||||
<div class="chat-msg-tip" v-show="msgInfo.type==$enums.MESSAGE_TYPE.RECALL">{{msgInfo.content}}</div> |
<div class="chat-msg-tip" v-show="msgInfo.type == $enums.MESSAGE_TYPE.RECALL">{{ msgInfo.content }}</div> |
||||
<div class="chat-msg-tip" v-show="msgInfo.type==$enums.MESSAGE_TYPE.TIP_TIME"> |
<div class="chat-msg-tip" v-show="msgInfo.type == $enums.MESSAGE_TYPE.TIP_TIME"> |
||||
{{$date.toTimeText(msgInfo.sendTime)}} |
{{ $date.toTimeText(msgInfo.sendTime) }} |
||||
</div> |
</div> |
||||
|
|
||||
<div class="chat-msg-normal" v-show="msgInfo.type>=0 && msgInfo.type<10" :class="{'chat-msg-mine':mine}"> |
<div class="chat-msg-normal" v-show="msgInfo.type >= 0 && msgInfo.type < 10" :class="{ 'chat-msg-mine': mine }"> |
||||
<div class="head-image"> |
<div class="head-image"> |
||||
<head-image :name="showName" :size="40" :url="headImage" :id="msgInfo.sendId"></head-image> |
<head-image :name="showName" :size="40" :url="headImage" :id="msgInfo.sendId"></head-image> |
||||
</div> |
</div> |
||||
<div class="chat-msg-content"> |
<div class="chat-msg-content"> |
||||
<div v-show="mode==1 && msgInfo.groupId && !msgInfo.selfSend" class="chat-msg-top"> |
<div v-show="mode == 1 && msgInfo.groupId && !msgInfo.selfSend" class="chat-msg-top"> |
||||
<span>{{showName}}</span> |
<span>{{ showName }}</span> |
||||
</div> |
</div> |
||||
<div v-show="mode==2" class="chat-msg-top"> |
<div v-show="mode == 2" class="chat-msg-top"> |
||||
<span>{{showName}}</span> |
<span>{{ showName }}</span> |
||||
<span>{{$date.toTimeText(msgInfo.sendTime)}}</span> |
<span>{{ $date.toTimeText(msgInfo.sendTime) }}</span> |
||||
</div> |
</div> |
||||
<div class="chat-msg-bottom" @contextmenu.prevent="showRightMenu($event)"> |
<div class="chat-msg-bottom" @contextmenu.prevent="showRightMenu($event)"> |
||||
<span class="chat-msg-text" v-if="msgInfo.type==$enums.MESSAGE_TYPE.TEXT" |
<div ref="chatMsgBox"> |
||||
v-html="$emo.transform(msgInfo.content)"></span> |
<span class="chat-msg-text" v-if="msgInfo.type == $enums.MESSAGE_TYPE.TEXT" |
||||
<div class="chat-msg-image" v-if="msgInfo.type==$enums.MESSAGE_TYPE.IMAGE"> |
v-html="$emo.transform(msgInfo.content)"></span> |
||||
<div class="img-load-box" v-loading="loading" element-loading-text="上传中.." |
<div class="chat-msg-image" v-if="msgInfo.type == $enums.MESSAGE_TYPE.IMAGE"> |
||||
element-loading-background="rgba(0, 0, 0, 0.4)"> |
<div class="img-load-box" v-loading="loading" element-loading-text="上传中.." |
||||
<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl" |
element-loading-background="rgba(0, 0, 0, 0.4)"> |
||||
@click="showFullImageBox()" /> |
<img class="send-image" :src="JSON.parse(msgInfo.content).thumbUrl" |
||||
</div> |
@click="showFullImageBox()" /> |
||||
<span title="发送失败" v-show="loadFail" @click="onSendFail" |
|
||||
class="send-fail el-icon-warning"></span> |
|
||||
</div> |
|
||||
<div class="chat-msg-file" v-if="msgInfo.type==$enums.MESSAGE_TYPE.FILE"> |
|
||||
<div class="chat-file-box" v-loading="loading"> |
|
||||
<div class="chat-file-info"> |
|
||||
<el-link class="chat-file-name" :underline="true" target="_blank" type="primary" |
|
||||
:href="data.url">{{data.name}}</el-link> |
|
||||
<div class="chat-file-size">{{fileSize}}</div> |
|
||||
</div> |
</div> |
||||
<div class="chat-file-icon"> |
<span title="发送失败" v-show="loadFail" @click="onSendFail" |
||||
<span type="primary" class="el-icon-document"></span> |
class="send-fail el-icon-warning"></span> |
||||
|
</div> |
||||
|
<div class="chat-msg-file" v-if="msgInfo.type == $enums.MESSAGE_TYPE.FILE"> |
||||
|
<div class="chat-file-box" v-loading="loading"> |
||||
|
<div class="chat-file-info"> |
||||
|
<el-link class="chat-file-name" :underline="true" target="_blank" type="primary" |
||||
|
:href="data.url">{{ data.name }}</el-link> |
||||
|
<div class="chat-file-size">{{ fileSize }}</div> |
||||
|
</div> |
||||
|
<div class="chat-file-icon"> |
||||
|
<span type="primary" class="el-icon-document"></span> |
||||
|
</div> |
||||
</div> |
</div> |
||||
|
<span title="发送失败" v-show="loadFail" @click="onSendFail" |
||||
|
class="send-fail el-icon-warning"></span> |
||||
</div> |
</div> |
||||
<span title="发送失败" v-show="loadFail" @click="onSendFail" |
|
||||
class="send-fail el-icon-warning"></span> |
|
||||
</div> |
</div> |
||||
<div class="chat-msg-voice" v-if="msgInfo.type==$enums.MESSAGE_TYPE.AUDIO" |
<div class="chat-msg-voice" v-if="msgInfo.type == $enums.MESSAGE_TYPE.AUDIO" @click="onPlayVoice()"> |
||||
@click="onPlayVoice()"> |
|
||||
<audio controls :src="JSON.parse(msgInfo.content).url"></audio> |
<audio controls :src="JSON.parse(msgInfo.content).url"></audio> |
||||
</div> |
</div> |
||||
<span class="chat-readed" v-show="msgInfo.selfSend && !msgInfo.groupId |
<span class="chat-readed" v-show="msgInfo.selfSend && !msgInfo.groupId |
||||
&& msgInfo.status==$enums.MESSAGE_STATUS.READED">已读</span> |
&& msgInfo.status == $enums.MESSAGE_STATUS.READED">已读</span> |
||||
<span class="chat-unread" v-show="msgInfo.selfSend && !msgInfo.groupId |
<span class="chat-unread" v-show="msgInfo.selfSend && !msgInfo.groupId |
||||
&& msgInfo.status!=$enums.MESSAGE_STATUS.READED">未读</span> |
&& msgInfo.status != $enums.MESSAGE_STATUS.READED">未读</span> |
||||
|
<div class="chat-receipt" v-show="msgInfo.receipt" @click="onShowReadedBox"> |
||||
|
<span v-if="msgInfo.readedCount>=0">{{msgInfo.readedCount}}人已读</span> |
||||
|
<span v-else class="icon iconfont icon-ok" title="全体已读"></span> |
||||
|
</div> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
|
||||
</div> |
</div> |
||||
<right-menu v-show="menu && rightMenu.show" :pos="rightMenu.pos" :items="menuItems" |
<right-menu v-show="menu && rightMenu.show" :pos="rightMenu.pos" :items="menuItems" @close="rightMenu.show = false" |
||||
@close="rightMenu.show=false" @select="onSelectMenu"></right-menu> |
@select="onSelectMenu"></right-menu> |
||||
|
<chat-group-readed ref="chatGroupReadedBox" :groupMembers="groupMembers"></chat-group-readed> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import HeadImage from "../common/HeadImage.vue"; |
import HeadImage from "../common/HeadImage.vue"; |
||||
import RightMenu from '../common/RightMenu.vue'; |
import RightMenu from '../common/RightMenu.vue'; |
||||
|
import ChatGroupReaded from './ChatGroupReaded.vue'; |
||||
export default { |
export default { |
||||
name: "messageItem", |
name: "messageItem", |
||||
components: { |
components: { |
||||
HeadImage, |
HeadImage, |
||||
RightMenu |
RightMenu, |
||||
|
ChatGroupReaded |
||||
|
}, |
||||
|
props: { |
||||
|
mode: { |
||||
|
type: Number, |
||||
|
default: 1 |
||||
}, |
}, |
||||
props: { |
mine: { |
||||
mode: { |
type: Boolean, |
||||
type: Number, |
required: true |
||||
default: 1 |
|
||||
}, |
|
||||
mine: { |
|
||||
type: Boolean, |
|
||||
required: true |
|
||||
}, |
|
||||
headImage: { |
|
||||
type: String, |
|
||||
required: true |
|
||||
}, |
|
||||
showName: { |
|
||||
type: String, |
|
||||
required: true |
|
||||
}, |
|
||||
msgInfo: { |
|
||||
type: Object, |
|
||||
required: true |
|
||||
}, |
|
||||
menu: { |
|
||||
type: Boolean, |
|
||||
default: true |
|
||||
} |
|
||||
}, |
}, |
||||
data() { |
headImage: { |
||||
return { |
type: String, |
||||
audioPlayState: 'STOP', |
required: true |
||||
rightMenu: { |
}, |
||||
show: false, |
showName: { |
||||
pos: { |
type: String, |
||||
x: 0, |
required: true |
||||
y: 0 |
}, |
||||
} |
msgInfo: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
}, |
||||
|
groupMembers: { |
||||
|
type: Array |
||||
|
}, |
||||
|
menu: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
audioPlayState: 'STOP', |
||||
|
rightMenu: { |
||||
|
show: false, |
||||
|
pos: { |
||||
|
x: 0, |
||||
|
y: 0 |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
onSendFail() { |
||||
|
this.$message.error("该文件已发送失败,目前不支持自动重新发送,建议手动重新发送") |
||||
}, |
}, |
||||
methods: { |
showFullImageBox() { |
||||
onSendFail() { |
let imageUrl = JSON.parse(this.msgInfo.content).originUrl; |
||||
this.$message.error("该文件已发送失败,目前不支持自动重新发送,建议手动重新发送") |
if (imageUrl) { |
||||
}, |
this.$store.commit('showFullImageBox', imageUrl); |
||||
showFullImageBox() { |
|
||||
let imageUrl = JSON.parse(this.msgInfo.content).originUrl; |
|
||||
if (imageUrl) { |
|
||||
this.$store.commit('showFullImageBox', imageUrl); |
|
||||
} |
|
||||
}, |
|
||||
onPlayVoice() { |
|
||||
if (!this.audio) { |
|
||||
this.audio = new Audio(); |
|
||||
} |
|
||||
this.audio.src = JSON.parse(this.msgInfo.content).url; |
|
||||
this.audio.play(); |
|
||||
this.onPlayVoice = 'RUNNING'; |
|
||||
}, |
|
||||
showRightMenu(e) { |
|
||||
this.rightMenu.pos = { |
|
||||
x: e.x, |
|
||||
y: e.y |
|
||||
}; |
|
||||
this.rightMenu.show = "true"; |
|
||||
}, |
|
||||
onSelectMenu(item) { |
|
||||
this.$emit(item.key.toLowerCase(), this.msgInfo); |
|
||||
} |
} |
||||
}, |
}, |
||||
computed: { |
onPlayVoice() { |
||||
loading() { |
if (!this.audio) { |
||||
return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "loading"; |
this.audio = new Audio(); |
||||
}, |
} |
||||
loadFail() { |
this.audio.src = JSON.parse(this.msgInfo.content).url; |
||||
return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "fail"; |
this.audio.play(); |
||||
}, |
this.onPlayVoice = 'RUNNING'; |
||||
data() { |
}, |
||||
return JSON.parse(this.msgInfo.content) |
showRightMenu(e) { |
||||
}, |
this.rightMenu.pos = { |
||||
fileSize() { |
x: e.x, |
||||
let size = this.data.size; |
y: e.y |
||||
if (size > 1024 * 1024) { |
}; |
||||
return Math.round(size / 1024 / 1024) + "M"; |
this.rightMenu.show = "true"; |
||||
} |
}, |
||||
if (size > 1024) { |
onSelectMenu(item) { |
||||
return Math.round(size / 1024) + "KB"; |
this.$emit(item.key.toLowerCase(), this.msgInfo); |
||||
} |
}, |
||||
return size + "B"; |
onShowReadedBox() { |
||||
}, |
let rect = this.$refs.chatMsgBox.getBoundingClientRect(); |
||||
menuItems() { |
this.$refs.chatGroupReadedBox.open(this.msgInfo, rect); |
||||
let items = []; |
} |
||||
|
}, |
||||
|
computed: { |
||||
|
loading() { |
||||
|
return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "loading"; |
||||
|
}, |
||||
|
loadFail() { |
||||
|
return this.msgInfo.loadStatus && this.msgInfo.loadStatus === "fail"; |
||||
|
}, |
||||
|
data() { |
||||
|
return JSON.parse(this.msgInfo.content) |
||||
|
}, |
||||
|
fileSize() { |
||||
|
let size = this.data.size; |
||||
|
if (size > 1024 * 1024) { |
||||
|
return Math.round(size / 1024 / 1024) + "M"; |
||||
|
} |
||||
|
if (size > 1024) { |
||||
|
return Math.round(size / 1024) + "KB"; |
||||
|
} |
||||
|
return size + "B"; |
||||
|
}, |
||||
|
menuItems() { |
||||
|
let items = []; |
||||
|
items.push({ |
||||
|
key: 'DELETE', |
||||
|
name: '删除', |
||||
|
icon: 'el-icon-delete' |
||||
|
}); |
||||
|
if (this.msgInfo.selfSend && this.msgInfo.id > 0) { |
||||
items.push({ |
items.push({ |
||||
key: 'DELETE', |
key: 'RECALL', |
||||
name: '删除', |
name: '撤回', |
||||
icon: 'el-icon-delete' |
icon: 'el-icon-refresh-left' |
||||
}); |
}); |
||||
if (this.msgInfo.selfSend && this.msgInfo.id > 0) { |
|
||||
items.push({ |
|
||||
key: 'RECALL', |
|
||||
name: '撤回', |
|
||||
icon: 'el-icon-refresh-left' |
|
||||
}); |
|
||||
} |
|
||||
return items; |
|
||||
} |
} |
||||
|
return items; |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped lang="scss"> |
<style lang="scss"> |
||||
.chat-msg-item { |
.chat-msg-item { |
||||
|
|
||||
.chat-msg-tip { |
.chat-msg-tip { |
||||
line-height: 50px; |
line-height: 50px; |
||||
font-size: 14px; |
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.chat-msg-normal { |
||||
|
position: relative; |
||||
|
font-size: 0; |
||||
|
padding-left: 60px; |
||||
|
min-height: 50px; |
||||
|
margin-top: 10px; |
||||
|
|
||||
|
.head-image { |
||||
|
position: absolute; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
} |
} |
||||
|
|
||||
.chat-msg-normal { |
.chat-msg-content { |
||||
position: relative; |
text-align: left; |
||||
font-size: 0; |
|
||||
padding-left: 60px; |
|
||||
min-height: 50px; |
|
||||
margin-top: 10px; |
|
||||
|
|
||||
.head-image { |
.send-fail { |
||||
position: absolute; |
color: #e60c0c; |
||||
width: 40px; |
font-size: 30px; |
||||
height: 40px; |
cursor: pointer; |
||||
top: 0; |
margin: 0 20px; |
||||
left: 0; |
|
||||
} |
} |
||||
|
|
||||
.chat-msg-content { |
.chat-msg-top { |
||||
text-align: left; |
display: flex; |
||||
|
flex-wrap: nowrap; |
||||
|
color: #333; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
|
||||
.send-fail { |
span { |
||||
color: #e60c0c; |
margin-right: 12px; |
||||
font-size: 30px; |
|
||||
cursor: pointer; |
|
||||
margin: 0 20px; |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
.chat-msg-top { |
.chat-msg-bottom { |
||||
display: flex; |
display: inline-block; |
||||
flex-wrap: nowrap; |
padding-right: 300px; |
||||
color: #333; |
|
||||
font-size: 14px; |
.chat-msg-text { |
||||
line-height: 20px; |
display: block; |
||||
|
position: relative; |
||||
span { |
line-height: 30px; |
||||
margin-right: 12px; |
margin-top: 3px; |
||||
|
padding: 7px; |
||||
|
background-color: white; |
||||
|
border-radius: 10px; |
||||
|
color: black; |
||||
|
display: block; |
||||
|
font-size: 16px; |
||||
|
text-align: left; |
||||
|
white-space: pre-wrap; |
||||
|
word-break: break-all; |
||||
|
box-shadow: 1px 1px 1px #c0c0f0; |
||||
|
|
||||
|
&:after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
left: -10px; |
||||
|
top: 13px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-style: solid dashed dashed; |
||||
|
border-color: white transparent transparent; |
||||
|
overflow: hidden; |
||||
|
border-width: 10px; |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
.chat-msg-bottom { |
.chat-msg-image { |
||||
display: inline-block; |
display: flex; |
||||
padding-right: 80px; |
flex-wrap: nowrap; |
||||
|
flex-direction: row; |
||||
.chat-msg-text { |
align-items: center; |
||||
display: block; |
|
||||
position: relative; |
.send-image { |
||||
line-height: 30px; |
min-width: 200px; |
||||
margin-top: 3px; |
min-height: 150px; |
||||
padding: 7px; |
max-width: 400px; |
||||
background-color: white; |
max-height: 300px; |
||||
border-radius: 10px; |
border: #dddddd solid 1px; |
||||
color: black; |
border: 5px solid #ccc; |
||||
display: block; |
border-radius: 6px; |
||||
font-size: 16px; |
cursor: pointer; |
||||
text-align: left; |
|
||||
white-space: pre-wrap; |
|
||||
word-break: break-all; |
|
||||
box-shadow: 1px 1px 1px #c0c0f0; |
|
||||
|
|
||||
&:after { |
|
||||
content: ""; |
|
||||
position: absolute; |
|
||||
left: -10px; |
|
||||
top: 13px; |
|
||||
width: 0; |
|
||||
height: 0; |
|
||||
border-style: solid dashed dashed; |
|
||||
border-color: white transparent transparent; |
|
||||
overflow: hidden; |
|
||||
border-width: 10px; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
.chat-msg-image { |
} |
||||
|
|
||||
|
.chat-msg-file { |
||||
|
display: flex; |
||||
|
flex-wrap: nowrap; |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
cursor: pointer; |
||||
|
padding-bottom: 5px; |
||||
|
.chat-file-box { |
||||
display: flex; |
display: flex; |
||||
flex-wrap: nowrap; |
flex-wrap: nowrap; |
||||
flex-direction: row; |
|
||||
align-items: center; |
align-items: center; |
||||
|
min-height: 80px; |
||||
.send-image { |
box-shadow: 5px 5px 2px #c0c0c0; |
||||
min-width: 200px; |
border: #dddddd solid 1px; |
||||
min-height: 150px; |
border-radius: 6px; |
||||
max-width: 400px; |
background-color: #eeeeee; |
||||
max-height: 300px; |
padding: 10px 15px; |
||||
border: #dddddd solid 1px; |
|
||||
border: 5px solid #ccc; |
.chat-file-info { |
||||
border-radius: 6px; |
flex: 1; |
||||
cursor: pointer; |
height: 100%; |
||||
|
text-align: left; |
||||
|
font-size: 14px; |
||||
|
|
||||
|
.chat-file-name { |
||||
|
display: inline-block; |
||||
|
min-width: 150px; |
||||
|
max-width: 300px; |
||||
|
font-size: 16px; |
||||
|
font-weight: 600; |
||||
|
margin-bottom: 15px; |
||||
|
white-space: pre-wrap; |
||||
|
word-break: break-all; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
|
.chat-file-icon { |
||||
|
font-size: 50px; |
||||
|
color: #d42e07; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
.chat-msg-file { |
.send-fail { |
||||
display: flex; |
color: #e60c0c; |
||||
flex-wrap: nowrap; |
font-size: 30px; |
||||
flex-direction: row; |
|
||||
align-items: center; |
|
||||
cursor: pointer; |
cursor: pointer; |
||||
|
margin: 0 20px; |
||||
|
} |
||||
|
|
||||
.chat-file-box { |
} |
||||
display: flex; |
|
||||
flex-wrap: nowrap; |
|
||||
align-items: center; |
|
||||
min-height: 80px; |
|
||||
box-shadow: 5px 5px 2px #c0c0c0; |
|
||||
border: #dddddd solid 1px; |
|
||||
border-radius: 6px; |
|
||||
background-color: #eeeeee; |
|
||||
padding: 10px 15px; |
|
||||
|
|
||||
.chat-file-info { |
|
||||
flex: 1; |
|
||||
height: 100%; |
|
||||
text-align: left; |
|
||||
font-size: 14px; |
|
||||
|
|
||||
.chat-file-name { |
|
||||
display: inline-block; |
|
||||
min-width: 150px; |
|
||||
max-width: 300px; |
|
||||
font-size: 16px; |
|
||||
font-weight: 600; |
|
||||
margin-bottom: 15px; |
|
||||
white-space: pre-wrap; |
|
||||
word-break: break-all; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.chat-file-icon { |
|
||||
font-size: 50px; |
|
||||
color: #d42e07; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.send-fail { |
.chat-msg-voice { |
||||
color: #e60c0c; |
font-size: 14px; |
||||
font-size: 30px; |
cursor: pointer; |
||||
cursor: pointer; |
|
||||
margin: 0 20px; |
|
||||
} |
|
||||
|
|
||||
|
audio { |
||||
|
height: 45px; |
||||
|
padding: 5px 0; |
||||
} |
} |
||||
|
} |
||||
|
|
||||
.chat-msg-voice { |
.chat-unread { |
||||
font-size: 14px; |
font-size: 12px; |
||||
cursor: pointer; |
color: #f23c0f; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
audio { |
.chat-readed { |
||||
height: 45px; |
font-size: 12px; |
||||
padding: 5px 0; |
color: #888; |
||||
} |
font-weight: 600; |
||||
} |
} |
||||
|
|
||||
.chat-unread { |
.chat-receipt{ |
||||
font-size: 10px; |
font-size: 13px; |
||||
color: #f23c0f; |
color: blue; |
||||
font-weight: 600; |
cursor: pointer; |
||||
} |
|
||||
|
|
||||
.chat-readed { |
.icon-ok { |
||||
font-size: 10px; |
font-size: 20px; |
||||
color: #888; |
color: green; |
||||
font-weight: 600; |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
&.chat-msg-mine { |
||||
|
text-align: right; |
||||
|
padding-left: 0; |
||||
|
padding-right: 60px; |
||||
|
|
||||
&.chat-msg-mine { |
.head-image { |
||||
|
left: auto; |
||||
|
right: 0; |
||||
|
} |
||||
|
|
||||
|
.chat-msg-content { |
||||
text-align: right; |
text-align: right; |
||||
padding-left: 0; |
|
||||
padding-right: 60px; |
|
||||
|
|
||||
.head-image { |
.chat-msg-top { |
||||
left: auto; |
flex-direction: row-reverse; |
||||
right: 0; |
|
||||
|
span { |
||||
|
margin-left: 12px; |
||||
|
margin-right: 0; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
.chat-msg-content { |
.chat-msg-bottom { |
||||
text-align: right; |
padding-left: 180px; |
||||
|
padding-right: 0; |
||||
|
|
||||
.chat-msg-top { |
.chat-msg-text { |
||||
flex-direction: row-reverse; |
margin-left: 10px; |
||||
|
background-color: rgb(88, 127, 240); |
||||
|
color: #fff; |
||||
|
vertical-align: top; |
||||
|
box-shadow: 1px 1px 1px #ccc; |
||||
|
|
||||
span { |
&:after { |
||||
margin-left: 12px; |
left: auto; |
||||
margin-right: 0; |
right: -10px; |
||||
|
border-top-color: rgb(88, 127, 240); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
.chat-msg-bottom { |
.chat-msg-image { |
||||
padding-left: 80px; |
flex-direction: row-reverse; |
||||
padding-right: 0; |
} |
||||
|
|
||||
.chat-msg-text { |
|
||||
margin-left: 10px; |
|
||||
background-color: rgb(88, 127, 240); |
|
||||
color: #fff; |
|
||||
vertical-align: top; |
|
||||
box-shadow: 1px 1px 1px #ccc; |
|
||||
|
|
||||
&:after { |
|
||||
left: auto; |
|
||||
right: -10px; |
|
||||
border-top-color: rgb(88, 127, 240); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.chat-msg-image { |
|
||||
flex-direction: row-reverse; |
|
||||
} |
|
||||
|
|
||||
.chat-msg-file { |
.chat-msg-file { |
||||
flex-direction: row-reverse; |
flex-direction: row-reverse; |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
@ -0,0 +1,131 @@ |
|||||
|
<template> |
||||
|
<uni-popup ref="popup" type="bottom"> |
||||
|
<view class="chat-group-readed"> |
||||
|
<view class="uni-padding-wrap uni-common-mt"> |
||||
|
<uni-segmented-control :current="current" :values="items" style-type="button" active-color="#587ff0" @clickItem="onClickItem"/> |
||||
|
</view> |
||||
|
<view class="content"> |
||||
|
<view v-if="current === 0"> |
||||
|
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> |
||||
|
<view v-for="m in readedMembers" :key="m.userId"> |
||||
|
<view class="member-item"> |
||||
|
<head-image :name="m.aliasName" :online="m.online" :url="m.headImage" |
||||
|
:size="90"></head-image> |
||||
|
<view class="member-name">{{ m.aliasName}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<view v-if="current === 1"> |
||||
|
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> |
||||
|
<view v-for="m in unreadMembers" :key="m.userId"> |
||||
|
<view class="member-item"> |
||||
|
<head-image :name="m.aliasName" :online="m.online" :url="m.headImage" |
||||
|
:size="90"></head-image> |
||||
|
<view class="member-name">{{ m.aliasName}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</uni-popup> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "chat-group-readed", |
||||
|
data() { |
||||
|
return { |
||||
|
items: ['已读', '未读'], |
||||
|
current: 0, |
||||
|
readedMembers: [], |
||||
|
unreadMembers: [] |
||||
|
}; |
||||
|
}, |
||||
|
props: { |
||||
|
msgInfo: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
}, |
||||
|
groupMembers: { |
||||
|
type: Array |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
open() { |
||||
|
this.$refs.popup.open(); |
||||
|
this.loadReadedUser(); |
||||
|
}, |
||||
|
loadReadedUser() { |
||||
|
this.readedMembers = []; |
||||
|
this.unreadMembers = []; |
||||
|
this.$http({ |
||||
|
url: `/message/group/findReadedUsers?groupId=${this.msgInfo.groupId}&messageId=${this.msgInfo.id}`, |
||||
|
method: 'Get' |
||||
|
}).then(userIds => { |
||||
|
this.groupMembers.forEach(member => { |
||||
|
// 发送者和已退群的不显示 |
||||
|
if (member.userId == this.msgInfo.sendId && member.quit) { |
||||
|
return; |
||||
|
} |
||||
|
// 区分已读还是未读 |
||||
|
if (userIds.find(userId => member.userId == userId)) { |
||||
|
this.readedMembers.push(member); |
||||
|
} else { |
||||
|
this.unreadMembers.push(member); |
||||
|
} |
||||
|
}) |
||||
|
this.items[0] = `已读(${this.readedMembers.length})`; |
||||
|
this.items[1] = `未读(${this.unreadMembers.length})`; |
||||
|
// 更新已读人数 |
||||
|
this.$store.commit("updateMessage", { |
||||
|
id: this.msgInfo.id, |
||||
|
groupId: this.msgInfo.groupId, |
||||
|
readedCount: this.readedMembers.length |
||||
|
}) |
||||
|
}) |
||||
|
}, |
||||
|
onClickItem(e){ |
||||
|
this.current = e.currentIndex; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.chat-group-readed { |
||||
|
position: relative; |
||||
|
border: #dddddd solid 1rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
background-color: white; |
||||
|
padding: 10rpx; |
||||
|
border-radius: 15rpx; |
||||
|
.scroll-bar { |
||||
|
height: 800rpx; |
||||
|
} |
||||
|
|
||||
|
.member-item { |
||||
|
height: 120rpx; |
||||
|
display: flex; |
||||
|
position: relative; |
||||
|
padding: 0 30rpx; |
||||
|
align-items: center; |
||||
|
background-color: white; |
||||
|
white-space: nowrap; |
||||
|
|
||||
|
.member-name { |
||||
|
flex: 1; |
||||
|
padding-left: 20rpx; |
||||
|
font-size: 30rpx; |
||||
|
font-weight: 600; |
||||
|
line-height: 60rpx; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
</style> |
||||
Binary file not shown.
Loading…
Reference in new issue