u
This commit is contained in:
1 parent
52b16ed2b0
commit
c26d1b3c81
15 files changed
+305
-32
No files matched your search
+8
@@ -6,8 +6,10 @@ import cn.link56.company.model.dto.AddCompanyUserDTO;
|
|||||||
import cn.link56.company.model.dto.DeleteCompanyUserDTO;
|
import cn.link56.company.model.dto.DeleteCompanyUserDTO;
|
||||||
import cn.link56.company.model.dto.QueryCompanyUserDTO;
|
import cn.link56.company.model.dto.QueryCompanyUserDTO;
|
||||||
import cn.link56.company.model.dto.UpdateCompanyUserDTO;
|
import cn.link56.company.model.dto.UpdateCompanyUserDTO;
|
||||||
|
import cn.link56.company.model.vo.ChatUserVO;
|
||||||
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
||||||
import cn.link56.company.service.CompanyUserService;
|
import cn.link56.company.service.CompanyUserService;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -43,4 +45,10 @@ public class CompanyUserController {
|
|||||||
companyUserService.deleteCompanyUser(deleteCompanyUserDTO);
|
companyUserService.deleteCompanyUser(deleteCompanyUserDTO);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/chat/{companyId}")
|
||||||
|
public Result<List<ChatUserVO>> listCompanyChatUsers(@PathVariable("companyId") Long companyId){
|
||||||
|
List<ChatUserVO> data = companyUserService.listCompanyChatUsers(companyId);
|
||||||
|
return Result.success(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package cn.link56.company.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ChatUserVO {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String position;
|
||||||
|
private String avatar;
|
||||||
|
private Boolean isOnline;
|
||||||
|
}
|
||||||
+23
@@ -12,7 +12,9 @@ import cn.link56.company.model.dto.QueryCompanyUserDTO;
|
|||||||
import cn.link56.company.model.dto.UpdateCompanyUserDTO;
|
import cn.link56.company.model.dto.UpdateCompanyUserDTO;
|
||||||
import cn.link56.company.model.po.Company;
|
import cn.link56.company.model.po.Company;
|
||||||
import cn.link56.company.model.po.CompanyUser;
|
import cn.link56.company.model.po.CompanyUser;
|
||||||
|
import cn.link56.company.model.vo.ChatUserVO;
|
||||||
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
import cn.link56.company.model.vo.QueryCompanyUserVO;
|
||||||
|
import cn.link56.im.storage.SessionManager;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
@@ -24,6 +26,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -304,4 +307,24 @@ public class CompanyUserService extends ServiceImpl<CompanyUserMapper, CompanyUs
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<ChatUserVO> listCompanyChatUsers(Long companyId) {
|
||||||
|
LambdaQueryWrapper<CompanyUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(CompanyUser::getCompanyId, companyId)
|
||||||
|
.eq(CompanyUser::getCanuse, "1");
|
||||||
|
|
||||||
|
List<CompanyUser> companyUsers = companyUserMapper.selectList(queryWrapper);
|
||||||
|
|
||||||
|
List<ChatUserVO> result = new ArrayList<>(companyUsers.size());
|
||||||
|
|
||||||
|
companyUsers.forEach(item -> {
|
||||||
|
ChatUserVO chatUserVO = new ChatUserVO();
|
||||||
|
BeanUtils.copyProperties(item, chatUserVO);
|
||||||
|
chatUserVO.setIsOnline(SessionManager.isOnline(item.getId()));
|
||||||
|
|
||||||
|
result.add(chatUserVO);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -216,9 +216,6 @@ public class MessageHandler {
|
|||||||
groupMessageVO.setSenderName(senderName);
|
groupMessageVO.setSenderName(senderName);
|
||||||
groupMessageVO.setSenderAvatar(senderAvatar);
|
groupMessageVO.setSenderAvatar(senderAvatar);
|
||||||
|
|
||||||
// 通知建立群组关系
|
|
||||||
|
|
||||||
|
|
||||||
// 查询该群在线的用户
|
// 查询该群在线的用户
|
||||||
List<Long> onlineGroupUsers = imGroupUserService.getGroupOnlineUserIds(groupId);
|
List<Long> onlineGroupUsers = imGroupUserService.getGroupOnlineUserIds(groupId);
|
||||||
MessageWriter.broadcastToUsers(onlineGroupUsers,MessageResponse.success(MessageTypeEnums.GROUP,groupId).setData(groupMessageVO));
|
MessageWriter.broadcastToUsers(onlineGroupUsers,MessageResponse.success(MessageTypeEnums.GROUP,groupId).setData(groupMessageVO));
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
VITE_SERVER_BASEURL = 'http://127.0.0.1:8084'
|
VITE_SERVER_BASEURL = 'http://192.168.1.137:8084'
|
||||||
VITE_WEBSOCKET_BASEURL = 'ws://127.0.0.1:8090/ws'
|
VITE_WEBSOCKET_BASEURL = 'ws://192.168.1.137:8090/ws'
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { CompanyChatUser } from '@/types/company/user'
|
||||||
|
import { Search } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { listCompanyChatUsersApi } from '@/request/api/company/user'
|
||||||
|
import { createFriendApi } from '@/request/api/im'
|
||||||
|
import { useUserStore } from '@/store/user'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
companyId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
|
const users = ref<CompanyChatUser[]>([])
|
||||||
|
|
||||||
|
function onPopoverShow() {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
listCompanyChatUsersApi(props.companyId).then((res) => {
|
||||||
|
if (res.isSuccess) {
|
||||||
|
users.value = res.data
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ElMessage.error(res.message || '获取聊天用户失败,请稍后再试')
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
ElMessage.error('获取聊天用户失败,请稍后再试')
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPopoverHide() {
|
||||||
|
loading.value = true
|
||||||
|
users.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectUser(user: CompanyChatUser) {
|
||||||
|
console.log('选择用户:', user)
|
||||||
|
if (userStore.user.id === user.id) {
|
||||||
|
ElMessage.warning('无法与自己发起聊天')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
createFriendApi({
|
||||||
|
friendId: user.id,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.isSuccess) {
|
||||||
|
router.push(`/im/private/${user.id}`)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ElMessage.error(res.message || '发起聊天失败,请稍后再试')
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
ElMessage.error('发起聊天失败,请稍后再试')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-popover
|
||||||
|
placement="bottom-start"
|
||||||
|
trigger="click"
|
||||||
|
width="280"
|
||||||
|
:popper-style="{
|
||||||
|
padding: '0px',
|
||||||
|
}"
|
||||||
|
|
||||||
|
@after-enter="onPopoverShow"
|
||||||
|
@after-leave="onPopoverHide"
|
||||||
|
>
|
||||||
|
<div v-if="loading" class="loading-wrapper">
|
||||||
|
<SvgIcon icon="svg-spinners:ring-resize" :size="20" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="users.length === 0" class="empty-wrapper">
|
||||||
|
<SvgIcon icon="streamline:interface-content-file-empty-common-file" :size="20" />
|
||||||
|
<span class="empty-tip">
|
||||||
|
暂无聊天用户
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="user-list">
|
||||||
|
<div
|
||||||
|
v-for="user in users"
|
||||||
|
:key="user.id"
|
||||||
|
class="user-item"
|
||||||
|
@click="selectUser(user)"
|
||||||
|
>
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<SafeImage class="avatar" :url="user.avatar" :size="40" round />
|
||||||
|
<span
|
||||||
|
class="status"
|
||||||
|
:class="{ online: user.isOnline }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<div class="name">
|
||||||
|
{{ user.name }}
|
||||||
|
</div>
|
||||||
|
<div v-if="user.position" class="title">
|
||||||
|
{{ user.position }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #reference>
|
||||||
|
<slot />
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.loading-wrapper,
|
||||||
|
.empty-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-wrapper {
|
||||||
|
flex-direction: column;
|
||||||
|
.empty-tip {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-search {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-list {
|
||||||
|
max-height: 320px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--el-fill-color-light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 在线状态点 */
|
||||||
|
.status {
|
||||||
|
position: absolute;
|
||||||
|
right: -4px;
|
||||||
|
bottom: -2px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #c0c4cc; // 离线
|
||||||
|
border: 2px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status.online {
|
||||||
|
background-color: #67c23a; // 在线(Element Plus 绿色)
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -60,7 +60,7 @@ function isActive(path: string) {
|
|||||||
<div class="action">
|
<div class="action">
|
||||||
<i-tdesign:search class="action-item" />
|
<i-tdesign:search class="action-item" />
|
||||||
|
|
||||||
<i-tdesign:notification v-if="userStore.isLogined" class="action-item" @click="navigateTo('/im')" />
|
<SvgIcon icon="solar:chat-round-line-linear" class="action-item" @click="navigateTo('/im')" />
|
||||||
|
|
||||||
<template v-if="userStore.isLogined">
|
<template v-if="userStore.isLogined">
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AddCompanyUserParams, CompanyUser, DeleteCompanyUserParams, QueryCompanyUserParams, UpdateCompanyUserParams } from '@/types/company/user'
|
import type { AddCompanyUserParams, CompanyChatUser, CompanyUser, DeleteCompanyUserParams, QueryCompanyUserParams, UpdateCompanyUserParams } from '@/types/company/user'
|
||||||
import { httpGet, httpPostJson } from '@/request/utils'
|
import { httpGet, httpPostJson } from '@/request/utils'
|
||||||
|
|
||||||
export function queryCompanyUserApi(params: QueryCompanyUserParams) {
|
export function queryCompanyUserApi(params: QueryCompanyUserParams) {
|
||||||
@@ -13,3 +13,7 @@ export function updateCompanyUserApi(params: UpdateCompanyUserParams) {
|
|||||||
export function deleteCompanyUserApi(params: DeleteCompanyUserParams) {
|
export function deleteCompanyUserApi(params: DeleteCompanyUserParams) {
|
||||||
return httpPostJson<void>('/company/user/delete', params)
|
return httpPostJson<void>('/company/user/delete', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listCompanyChatUsersApi(companyId: number) {
|
||||||
|
return httpGet<CompanyChatUser[]>(`/company/user/chat/${companyId}`)
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ declare module 'vue' {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AnchorMenu: typeof import('./../components/anchor-menu/index.vue')['default']
|
AnchorMenu: typeof import('./../components/anchor-menu/index.vue')['default']
|
||||||
AreaSelect: typeof import('./../components/AreaSelect.vue')['default']
|
AreaSelect: typeof import('./../components/AreaSelect.vue')['default']
|
||||||
|
ChatCompanyUserSelect: typeof import('../components/ChatUserSelect.vue')['default']
|
||||||
|
ChatUserSelect: typeof import('./../components/ChatUserSelect.vue')['default']
|
||||||
CustomEmpty: typeof import('./../components/CustomEmpty.vue')['default']
|
CustomEmpty: typeof import('./../components/CustomEmpty.vue')['default']
|
||||||
CustomLoading: typeof import('./../components/CustomLoading.vue')['default']
|
CustomLoading: typeof import('./../components/CustomLoading.vue')['default']
|
||||||
Directory: typeof import('./../components/wang-editor/modules/directory.vue')['default']
|
Directory: typeof import('./../components/wang-editor/modules/directory.vue')['default']
|
||||||
|
|||||||
@@ -57,3 +57,11 @@ export interface DeleteCompanyUserParams {
|
|||||||
companyId: number
|
companyId: number
|
||||||
ids: number[]
|
ids: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CompanyChatUser {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
position: string
|
||||||
|
avatar: string
|
||||||
|
isOnline: boolean
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import type { Company } from '@/types/company'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import ChatUserSelect from '@/components/ChatUserSelect.vue'
|
||||||
import QueryCompany from '@/components/QueryCompany.vue'
|
import QueryCompany from '@/components/QueryCompany.vue'
|
||||||
import { queryCompanyApi } from '@/request/api/company'
|
import { queryCompanyApi } from '@/request/api/company'
|
||||||
import { useCommonStore } from '@/store/common'
|
import { useCommonStore } from '@/store/common'
|
||||||
@@ -85,7 +86,7 @@ function toDetail(data: Company) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="company-tags">
|
<div class="company-tags">
|
||||||
<el-tag v-for="tag in item.industries" :key="tag" type="primary" effect="dark">
|
<el-tag v-for="tag in item.industries" :key="tag" type="primary">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,29 +98,29 @@ function toDetail(data: Company) {
|
|||||||
<div class="company-footer">
|
<div class="company-footer">
|
||||||
<div class="company-details">
|
<div class="company-details">
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<i-material-symbols:location-on />
|
<SvgIcon icon="material-symbols:location-on" />
|
||||||
<span>{{ item.areas.join('/') || '___' }}</span>
|
<span>{{ item.areas.join('/') || '___' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<i-material-symbols:mail />
|
<SvgIcon icon="material-symbols:mail" />
|
||||||
<span>{{ item.email || '___' }}</span>
|
<span>{{ item.email || '___' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<i-material-symbols:call />
|
<SvgIcon icon="material-symbols:call" />
|
||||||
<span>{{ item.phone || '___' }}</span>
|
<span>{{ item.phone || '___' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="company-actions">
|
<div class="company-actions">
|
||||||
|
<ChatUserSelect :company-id="item.id">
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
text
|
|
||||||
bg
|
|
||||||
>
|
>
|
||||||
<i-ri:message-3-fill />
|
<i-ri:message-3-fill />
|
||||||
<span class="button-text">
|
<span class="button-text">
|
||||||
发起聊天
|
发起聊天
|
||||||
</span>
|
</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
</ChatUserSelect>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ async function sendMessage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="im__chat-sendarea">
|
<div class="im__chat-sendarea">
|
||||||
<el-button type="primary" size="small" class="im__chat-send">
|
<el-button type="primary" size="small" class="im__chat-send" @click="sendMessage">
|
||||||
发送(回车)
|
发送(回车)
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ async function sendMessage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="im__chat-sendarea">
|
<div class="im__chat-sendarea">
|
||||||
<el-button type="primary" size="small" class="im__chat-send">
|
<el-button type="primary" size="small" class="im__chat-send" @click="sendMessage">
|
||||||
发送(回车)
|
发送(回车)
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ImFriend, ImGroup } from '@/types/im'
|
import type { ImFriend, ImGroup } from '@/types/im'
|
||||||
import { Search } from '@element-plus/icons-vue'
|
import { Search } from '@element-plus/icons-vue'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { getFriendProfileApi, getGroupProfileApi, queryFriendApi, queryGroupApi, updateGroupUnreadApi, updatePrivateUnreadApi } from '@/request/api/im'
|
import { getFriendProfileApi, getGroupProfileApi, queryFriendApi, queryGroupApi, updateGroupUnreadApi, updatePrivateUnreadApi } from '@/request/api/im'
|
||||||
import { useUserStore } from '@/store/user'
|
import { useUserStore } from '@/store/user'
|
||||||
@@ -75,8 +75,10 @@ onMounted(() => {
|
|||||||
selectedChat.value = { type, id }
|
selectedChat.value = { type, id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
loadGroups()
|
loadGroups()
|
||||||
loadFriends()
|
loadFriends()
|
||||||
|
})
|
||||||
|
|
||||||
websocket.onMessage(async (msg) => {
|
websocket.onMessage(async (msg) => {
|
||||||
const { type, from, data } = msg
|
const { type, from, data } = msg
|
||||||
@@ -182,12 +184,12 @@ onMounted(() => {
|
|||||||
|
|
||||||
const currentGroupId = currentGroupChat.value?.groupId
|
const currentGroupId = currentGroupChat.value?.groupId
|
||||||
|
|
||||||
// 1️⃣ 查找本地群
|
// 1 查找本地群
|
||||||
const group = groupState.value.list.find(
|
const group = groupState.value.list.find(
|
||||||
item => item.groupId === groupId,
|
item => item.groupId === groupId,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 2️⃣ 本地没有 → 懒加载群 Profile
|
// 2 本地没有 → 懒加载群 Profile
|
||||||
if (!group) {
|
if (!group) {
|
||||||
try {
|
try {
|
||||||
const res = await getGroupProfileApi(groupId)
|
const res = await getGroupProfileApi(groupId)
|
||||||
@@ -216,18 +218,18 @@ onMounted(() => {
|
|||||||
if (!group) {
|
if (!group) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 3️⃣ 更新最近消息
|
// 3 更新最近消息
|
||||||
group.lastMessageTime = sendTime
|
group.lastMessageTime = sendTime
|
||||||
group.lastMessageContent = content
|
group.lastMessageContent = content
|
||||||
group.lastMessageType = contentType
|
group.lastMessageType = contentType
|
||||||
group.lastMessageSenderName = senderName
|
group.lastMessageSenderName = senderName
|
||||||
|
|
||||||
// 4️⃣ 未读处理(不是自己 & 当前群未打开)
|
// 4 未读处理(不是自己 & 当前群未打开)
|
||||||
if (senderId !== userId && groupId !== currentGroupId) {
|
if (senderId !== userId && groupId !== currentGroupId) {
|
||||||
group.unreadCount = (group.unreadCount || 0) + 1
|
group.unreadCount = (group.unreadCount || 0) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5️⃣ 当前群窗口处理
|
// 5 当前群窗口处理
|
||||||
if (currentGroupChat.value && currentGroupId === groupId) {
|
if (currentGroupChat.value && currentGroupId === groupId) {
|
||||||
currentGroupChat.value.lastMessageTime = sendTime
|
currentGroupChat.value.lastMessageTime = sendTime
|
||||||
currentGroupChat.value.lastMessageContent = content
|
currentGroupChat.value.lastMessageContent = content
|
||||||
@@ -244,7 +246,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6️⃣ 永远置顶
|
// 6 永远置顶
|
||||||
const index = groupState.value.list.findIndex(
|
const index = groupState.value.list.findIndex(
|
||||||
item => item.groupId === groupId,
|
item => item.groupId === groupId,
|
||||||
)
|
)
|
||||||
@@ -285,7 +287,20 @@ async function loadFriends() {
|
|||||||
state.hasMore = state.list.length < total
|
state.hasMore = state.list.length < total
|
||||||
state.page++
|
state.page++
|
||||||
|
|
||||||
selectedChat.value.id = null
|
if (isPrivateChat.value && selectedChat.value.id) {
|
||||||
|
const index = state.list.findIndex(
|
||||||
|
item => item.friendId === selectedChat.value.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
const [target] = state.list.splice(index, 1)
|
||||||
|
|
||||||
|
// 移动到第一位
|
||||||
|
state.list.unshift(target)
|
||||||
|
|
||||||
|
currentFriendChat.value = target
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -323,8 +338,6 @@ async function loadGroups() {
|
|||||||
state.total = total
|
state.total = total
|
||||||
state.hasMore = state.list.length < total
|
state.hasMore = state.list.length < total
|
||||||
state.page++
|
state.page++
|
||||||
|
|
||||||
selectedChat.value.id = null
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@@ -395,9 +408,6 @@ function onGroupChat(group: ImGroup) {
|
|||||||
userId,
|
userId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(isGroupChat.value)
|
|
||||||
console.log(currentGroupChat.value)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -534,7 +544,7 @@ function onGroupChat(group: ImGroup) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="im__window">
|
<div v-if="selectedChat.id && selectedChat.type" class="im__window">
|
||||||
<div class="im__window-header">
|
<div class="im__window-header">
|
||||||
<div class="im__window-peer">
|
<div class="im__window-peer">
|
||||||
<div class="im__window-peer-info">
|
<div class="im__window-peer-info">
|
||||||
@@ -603,8 +613,8 @@ function onGroupChat(group: ImGroup) {
|
|||||||
width: 1300px;
|
width: 1300px;
|
||||||
min-height: 750px;
|
min-height: 750px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
border-radius: 2px;
|
// border-radius: 2px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default defineConfig((configEnv) => {
|
|||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 8080,
|
port: 8089,
|
||||||
open: true,
|
open: true,
|
||||||
// 用于配置开发服务器启动时预加载的一些客户端文件,以提高初次加载的速度。
|
// 用于配置开发服务器启动时预加载的一些客户端文件,以提高初次加载的速度。
|
||||||
warmup: {
|
warmup: {
|
||||||
|
|||||||
Reference in new issue
Block a user