新增:IM 弹框

This commit is contained in:
安浩浩
2024-04-24 20:54:13 +08:00
parent ffc81621d7
commit de27cfa8f6
23 changed files with 1012 additions and 3 deletions

View File

@ -0,0 +1,5 @@
<script setup lang="ts"></script>
<template></template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,263 @@
<script setup lang="ts">
import { formatDate } from '@/utils/formatTime'
import { reactive } from 'vue'
//取好友列表(主要使用好友下的用户属性相关)
const friendList = reactive([
{
friendKey: {
avatarurl: '',
nickName: ''
}
}
])
//取会话数据
const conversationList = reactive({
conversationKey: {
conversationInfo: {
avatarUrl: '',
name: '',
conversationType: 0
},
latestMessage: {
msg: ''
},
latestSendTime: 0,
unreadMessageNum: 0,
isMention: false
}
})
//处理会话name
const handleConversationName = computed(() => {
return ''
})
//处理lastmsg的from昵称
const handleLastMsgNickName = computed(() => {
return ''
})
//普通会话
const checkedConverItemIndex = ref(null)
const toChatMessage = (item, itemKey, index) => {
checkedConverItemIndex.value = index
}
//删除某条会话
const deleteConversation = (itemKey) => {}
</script>
<template>
<!-- 普通会话 -->
<template v-if="Object.keys(conversationList).length > 0">
<li
v-for="(item, itemKey, index) in conversationList"
:key="itemKey"
@click="toChatMessage(item, itemKey, index)"
:style="{
background: checkedConverItemIndex === index ? '#E5E5E5' : ''
}"
>
<el-popover
popper-class="conversation_popover"
placement="right-end"
trigger="contextmenu"
:show-arrow="false"
:offset="-10"
>
<template #reference>
<div class="session_list_item">
<div class="item_body item_left">
<div class="session_other_avatar">
<el-avatar
:size="34"
:src="
friendList[item.conversationKey] && friendList[item.conversationKey].avatarurl
? friendList[item.conversationKey].avatarurl
: item.conversationInfo.avatarUrl
"
/>
</div>
</div>
<div class="item_body item_main">
<div class="name"> 好友 </div>
<div class="last_msg_body">
<span class="last_msg_body_mention" v-if="item.isMention">[有人@]</span>
<span v-show="item.conversationType === 2">好友</span>
{{ item.latestMessage.msg }}
</div>
</div>
<div class="item_body item_right">
<span class="time">{{ formatDate(item.latestSendTime, 'MM/DD/HH:mm') }}</span>
<span class="unReadNum_box" v-if="item.unreadMessageNum >= 1">
<sup
class="unReadNum_count"
v-text="item.unreadMessageNum >= 99 ? '99+' : item.unreadMessageNum"
></sup>
</span>
</div>
</div>
</template>
<template #default>
<div class="session_list_delete" @click="deleteConversation(itemKey)"> 删除会话 </div>
</template>
</el-popover>
</li>
</template>
<template v-else>
<el-empty description="暂无最近会话" />
</template>
</template>
<style scoped lang="scss">
.session_list {
position: relative;
height: 100%;
padding: 0;
margin: 0;
}
.offline_hint {
width: 100%;
height: 30px;
text-align: center;
line-height: 30px;
color: #f35f81;
background: #fce7e8;
font-size: 7px;
.plaint_icon {
display: inline-block;
width: 15px;
height: 15px;
color: #e5e5e5;
text-align: center;
line-height: 15px;
font-size: 7px;
font-weight: bold;
background: #e6686e;
border-radius: 50%;
}
}
.session_list .session_list_item {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
height: 66px;
background: #f0f0f0;
color: var(--el-color-primary);
border-bottom: 1px solid var(--el-border-color);
cursor: pointer;
&:hover {
background: #e5e5e5;
}
.item_body {
display: flex;
height: 100%;
}
.item_left {
flex-direction: row;
align-items: center;
justify-content: center;
margin-left: 14px;
margin-right: 10px;
}
.item_main {
width: 225px;
max-width: 225px;
height: 34px;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
.name {
min-width: 56px;
max-width: 180px;
height: 17px;
font-weight: 400;
font-size: 14px;
/* identical to box height */
color: #333333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.last_msg_body {
max-width: 185px;
height: 17px;
font-weight: 400;
font-size: 12px;
line-height: 17px;
letter-spacing: 0.3px;
color: #a3a3a3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.last_msg_body_mention {
font-size: 12px;
line-height: 17px;
font-weight: bold;
color: red;
}
}
.item_right {
width: 25%;
height: 34px;
flex-direction: column;
align-items: flex-end;
margin-right: 10px;
.time {
font-size: 10px;
font-weight: 400;
font-size: 10px;
line-height: 14px;
letter-spacing: 0.25px;
color: #a3a3a3;
}
.unReadNum_box {
margin-top: 10px;
vertical-align: middle;
.unReadNum_count {
display: inline-block;
min-width: 20px;
height: 20px;
padding: 0 6px;
color: #fff;
font-weight: normal;
font-size: 12px;
line-height: 20px;
white-space: nowrap;
text-align: center;
background: #f5222d;
border-radius: 10px;
box-sizing: border-box;
}
}
}
}
.session_list_item_active {
background: #d2d2d2;
}
.session_list .session_list_item + .list_item {
margin-top: 10px;
}
.session_list_delete {
cursor: pointer;
transition: all 0.5s;
&:hover {
background: #e1e1e1;
}
}
</style>

View File

@ -0,0 +1,42 @@
<script setup lang="ts">
/* 搜索框组件 */
import SearchInput from '@/components/SearchInput/index.vue'
/* 欢迎页 */
import Welcome from '@/components/Welcome/index.vue'
import ConversationList from '../Conversation/components/ConversationList.vue'
</script>
<template>
<el-container style="height: 100%">
<el-aside class="chat_conversation_box">
<!-- 搜索组件 -->
<SearchInput :searchType="'conversation'" />
<div class="chat_conversation_list">
<ConversationList />
</div>
</el-aside>
<el-main class="chat_conversation_main_box">
<router-view />
<Welcome />
</el-main>
</el-container>
</template>
<style lang="scss" scoped>
.chat_conversation_box {
position: relative;
background: #cfdbf171;
overflow: hidden;
min-width: 324px;
.chat_conversation_list {
height: calc(100% - 60px);
}
}
.chat_conversation_main_box {
position: relative;
width: 100%;
height: 100%;
padding: 0;
}
</style>

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
</template>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,287 @@
<script setup lang="ts">
/* route */
import { useRoute } from 'vue-router'
/* 取用户头像 */
import router from '@/router'
import highlightConversation from '@/assets/imgs/tabbar/highlightconversation.png'
import grayConversation from '@/assets/imgs/tabbar/grayconversation.png'
import highlightContacts from '@/assets/imgs/tabbar/higtlightcontacts.png'
import grayContacts from '@/assets/imgs/tabbar/graycontacts.png'
import avatarImg from '@/assets/imgs/avatar.gif'
import { useUserStore } from '@/store/modules/user'
const userStore = useUserStore()
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
/* tabular icon 路由跳转 */
const skipRouterName = ref('conversation')
const changeSkipRouterName = (routerName: string) => {
router.push(`/im/${routerName}`)
}
const route = useRoute()
// 监听路由变化
watch(
() => route.path,
(newPath) => {
console.log('>>>>>newPath', newPath)
if (newPath.includes('/im/conversation')) {
skipRouterName.value = 'conversation'
}
if (newPath.includes('/im/contacts')) {
console.log('>>>>>存在赋值为联系人样式')
skipRouterName.value = 'contacts'
}
}
)
</script>
<template>
<!-- 头像 -->
<div class="chat_avatar">
<ElAvatar :src="avatar" alt="" class="w-[calc(var(--logo-height)-25px)] rounded-[50%]" />
</div>
<!-- 去往会话 -->
<div class="chat_conversation chat_icon_box" @click="changeSkipRouterName('conversation')">
<div class="img_box">
<img
:src="skipRouterName === 'conversation' ? highlightConversation : grayConversation"
alt=""
/>
</div>
</div>
<!-- 去往联系人 -->
<div class="chat_contacts chat_icon_box" @click="changeSkipRouterName('contacts')">
<img
class="chat_contacts_icon"
:src="skipRouterName === 'contacts' ? highlightContacts : grayContacts"
alt=""
/>
</div>
</template>
<style lang="scss" scoped>
.chat_avatar {
margin-top: 43px;
position: relative;
width: 44px;
height: 44px;
transition: all 0.3s;
&:hover {
transform: scale(1.3);
}
span {
display: inline-block;
width: 100%;
height: 100%;
}
.online_status {
position: absolute;
right: 2px;
bottom: 2px;
display: inline-block;
width: 6px;
height: 6px;
border: 2px solid #fff;
background: #fff;
border-radius: 50%;
transition: all 0.3s;
cursor: pointer;
&:hover {
transform: scale(1.2);
}
}
}
.chat_icon_box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 67px;
text-align: center;
line-height: 67px;
margin: 4px 0;
}
.chat_conversation {
.img_box {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
img {
display: inline-block;
width: 27px;
height: 27px;
transition: all 0.5s;
&:hover {
transform: scale(1.3);
}
}
.badge {
position: absolute;
right: 0;
top: 8px;
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
background: red;
}
}
}
.chat_contacts {
img {
display: inline-block;
width: 27px;
height: 27px;
transition: all 0.5s;
&:hover {
transform: scale(1.3);
}
}
}
.chat_settings {
position: absolute;
bottom: 92px;
font-size: 30px;
color: #8e8e8e;
cursor: pointer;
transition: all 0.5s;
&:hover {
color: #1b83f9;
transform: scale(1.3);
}
.chat_setting_item {
width: 100%;
height: 30px;
}
}
.more_settings {
position: absolute;
bottom: 46px;
color: #8e8e8e;
cursor: pointer;
transition: all 0.5s;
&:hover {
transform: scale(1.3);
}
}
.setting_fun_list {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.func_item {
display: flex;
flex-direction: row;
align-items: center;
// justify-content: space-around;
width: 101px;
height: 40px;
border-radius: 3px;
&:hover {
background-color: #f2f2f2;
}
.settting_fun_icon {
display: flex;
align-items: center;
justify-content: center;
margin-left: 5px;
img {
width: 20px;
height: 20px;
}
}
.setting_fun_text {
display: inline-block;
text-align: center;
margin-left: 12px;
height: 20px;
width: 58px;
font-weight: 400;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.4px;
color: #333333;
cursor: pointer;
}
.apply_groups {
display: flex;
flex-direction: column;
}
}
}
.settting_fun_icon {
font-size: 20px;
}
.line {
display: inline-block;
width: 69px;
height: 1px;
border: 1px solid rgba(0, 0, 0, 0.0462467);
}
.components {
::v-deep .edit_userinfo_diglog {
border-radius: 4px;
overflow: hidden;
}
::v-deep .setting_func_diglog > .el-dialog__body {
padding: 28px 24px 24px 24px;
}
::v-deep .setting_func_diglog > .el-dialog__header {
background: #f2f2f2;
margin: 0;
}
::v-deep .edit_userinfo_diglog > .el-dialog__header {
padding: 0;
margin-right: 0;
}
::v-deep .edit_userinfo_diglog > .el-dialog__body {
padding: 0;
border-radius: 4px;
}
::v-deep .login_diglog > .el-dialog__header {
background: #f2f2f2;
margin: 0;
}
::v-deep .personal_setting_card > .el-dialog__header {
background: #f2f2f2;
margin: 0;
}
}
</style>

53
src/views/im/index.vue Normal file
View File

@ -0,0 +1,53 @@
<script lang="ts" setup>
import NavBar from './NavBar/index.vue'
defineOptions({ name: 'IM' })
</script>
<template>
<div class="app-container">
<el-container class="chat_container">
<el-aside class="chat_nav_bar" width="72px">
<NavBar />
</el-aside>
<el-main class="chat_main_box">
<router-view />
</el-main>
</el-container>
</div>
</template>
<style lang="scss" scoped>
.app-container {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background-size: cover;
backdrop-filter: blur(5px);
.chat_container {
width: 85%;
height: 95%;
background: #fff;
position: relative;
top: 50%;
transform: translateY(-50%);
margin: auto auto;
border-radius: 5px;
.chat_nav_bar {
display: flex;
flex-direction: column;
align-items: center;
border-radius: 5px 0 0 5px;
width: 80px;
background: #262626;
overflow: hidden;
}
.chat_main_box {
padding: 0;
}
}
}
</style>