✨ feat(im): 前端的 IM 增加 Layout 整体布局
parent
518851ce74
commit
9fb796194e
|
|
@ -0,0 +1,53 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
// 群聊消息发送 Request VO
|
||||
export interface ImGroupMessageSendReqVO {
|
||||
clientMessageId: string // 客户端消息编号
|
||||
groupId: number // 群编号
|
||||
type: number // 消息类型
|
||||
content: string // 消息内容
|
||||
atUserIds?: number[] // @ 用户编号列表
|
||||
receiverUserIds?: number[] // 定向接收用户编号列表
|
||||
needReceipt?: boolean // 是否需要回执
|
||||
}
|
||||
|
||||
// 群聊消息 Response VO
|
||||
export interface ImGroupMessageRespVO {
|
||||
id: string // 消息编号
|
||||
clientMessageId: string // 客户端消息编号
|
||||
senderId: string // 发送人编号
|
||||
groupId: string // 群编号
|
||||
type: number // 消息类型
|
||||
content: string // 消息内容
|
||||
status: number // 消息状态
|
||||
sendTime: string // 发送时间
|
||||
atUserIds?: number[] // @ 用户编号列表
|
||||
receiverUserIds?: number[] // 定向接收用户编号列表
|
||||
receiptStatus?: number // 回执状态
|
||||
readCount?: number // 已读人数
|
||||
}
|
||||
|
||||
// 发送群聊消息
|
||||
export const sendGroupMessage = (data: ImGroupMessageSendReqVO) => {
|
||||
return request.post<ImGroupMessageRespVO>({ url: '/im/message/group/send', data })
|
||||
}
|
||||
|
||||
// 增量拉取群聊消息
|
||||
export const pullGroupMessages = (params: { minId: string; size: number }) => {
|
||||
return request.get<ImGroupMessageRespVO[]>({ url: '/im/message/group/pull', params })
|
||||
}
|
||||
|
||||
// 标记群聊消息已读
|
||||
export const readGroupMessages = (groupId: string) => {
|
||||
return request.put({ url: '/im/message/group/read', params: { groupId } })
|
||||
}
|
||||
|
||||
// 撤回群聊消息
|
||||
export const recallGroupMessage = (id: string) => {
|
||||
return request.delete({ url: '/im/message/group/recall', params: { id } })
|
||||
}
|
||||
|
||||
// 查询群消息已读用户列表
|
||||
export const getGroupReadUsers = (params: { groupId: string; messageId: string }) => {
|
||||
return request.get<string[]>({ url: '/im/message/group/read-users', params })
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
// 私聊消息发送 Request VO
|
||||
export interface ImPrivateMessageSendReqVO {
|
||||
clientMessageId: string // 客户端消息编号
|
||||
receiverId: number // 接收人编号
|
||||
type: number // 消息类型
|
||||
content: string // 消息内容
|
||||
}
|
||||
|
||||
// 私聊消息 Response VO
|
||||
export interface ImPrivateMessageRespVO {
|
||||
id: number // 消息编号
|
||||
clientMessageId: string // 客户端消息编号
|
||||
senderId: number // 发送人编号
|
||||
receiverId: number // 接收人编号
|
||||
type: number // 消息类型
|
||||
content: string // 消息内容
|
||||
status: number // 消息状态
|
||||
sendTime: string // 发送时间
|
||||
}
|
||||
|
||||
// 发送私聊消息
|
||||
export const sendPrivateMessage = (data: ImPrivateMessageSendReqVO) => {
|
||||
return request.post<ImPrivateMessageRespVO>({ url: '/im/message/private/send', data })
|
||||
}
|
||||
|
||||
// 增量拉取私聊消息
|
||||
export const pullPrivateMessages = (params: { minId: string; size: number }) => {
|
||||
return request.get<ImPrivateMessageRespVO[]>({ url: '/im/message/private/pull', params })
|
||||
}
|
||||
|
||||
// 标记私聊消息已读
|
||||
export const readPrivateMessages = (friendId: string) => {
|
||||
return request.put({ url: '/im/message/private/read', params: { friendId } })
|
||||
}
|
||||
|
||||
// 撤回私聊消息
|
||||
export const recallPrivateMessage = (id: string) => {
|
||||
return request.delete({ url: '/im/message/private/recall', params: { id } })
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="tsx">
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Message } from '@/layout/components//Message'
|
||||
import router from '@/router'
|
||||
import { Message } from '@/layout/components/Message'
|
||||
import { Collapse } from '@/layout/components/Collapse'
|
||||
import { UserInfo } from '@/layout/components/UserInfo'
|
||||
import { Screenfull } from '@/layout/components/Screenfull'
|
||||
|
|
@ -45,15 +45,19 @@ const locale = computed(() => appStore.getLocale)
|
|||
// 消息图标
|
||||
const message = computed(() => appStore.getMessage)
|
||||
|
||||
const goToChat = () => {
|
||||
window.open(window.location.host + '/chat', '_blank')
|
||||
}
|
||||
|
||||
// 租户切换权限
|
||||
const hasTenantVisitPermission = computed(
|
||||
() => import.meta.env.VITE_APP_TENANT_ENABLE === 'true' && checkPermi(['system:tenant:visit'])
|
||||
)
|
||||
|
||||
// 顶部聊天入口:用路由 name resolve 出完整 URL,在新标签页打开 IM 主页
|
||||
// 场景考虑:IM 是全屏沉浸式壳,如果在当前页 push 会把原来在用的后台管理界面挤掉;开新 Tab 更符合用户预期
|
||||
const goToChat = () => {
|
||||
// 用路由 name resolve 出完整 URL,在新标签页打开 IM 主页
|
||||
const { href } = router.resolve({ name: 'ImHome' })
|
||||
window.open(href, '_blank')
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ToolHeader',
|
||||
setup() {
|
||||
|
|
@ -75,18 +79,13 @@ export default defineComponent({
|
|||
</div>
|
||||
) : undefined}
|
||||
<div class="h-full flex items-center">
|
||||
<div onClick={goToChat}>
|
||||
<Icon
|
||||
icon="ep:chat-dot-round"
|
||||
color="var(--top-header-text-color)"
|
||||
class="custom-hover"
|
||||
/>
|
||||
</div>
|
||||
{hasTenantVisitPermission.value ? <TenantVisit /> : undefined}
|
||||
{screenfull.value ? (
|
||||
<Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
|
||||
) : undefined}
|
||||
{search.value ? <RouterSearch isModal={false} color="var(--top-header-text-color)"/> : undefined}
|
||||
{search.value ? (
|
||||
<RouterSearch isModal={false} color="var(--top-header-text-color)" />
|
||||
) : undefined}
|
||||
{size.value ? (
|
||||
<SizeDropdown class="custom-hover" color="var(--top-header-text-color)"></SizeDropdown>
|
||||
) : undefined}
|
||||
|
|
@ -99,6 +98,10 @@ export default defineComponent({
|
|||
{message.value ? (
|
||||
<Message class="custom-hover" color="var(--top-header-text-color)"></Message>
|
||||
) : undefined}
|
||||
{/* IM 聊天入口 */}
|
||||
<div class="custom-hover" onClick={goToChat}>
|
||||
<Icon color="var(--top-header-text-color)" size={18} icon="ep:chat-dot-round" />
|
||||
</div>
|
||||
<UserInfo></UserInfo>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Layout } from '@/utils/routerHelper'
|
||||
import ChatPage from '../../views/chat/ChatPage/Index.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
/**
|
||||
|
|
@ -747,11 +746,107 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||
component: () => import('@/views/iot/ota/firmware/detail/index.vue')
|
||||
}
|
||||
]
|
||||
}, {
|
||||
path: '/chat',
|
||||
component: ChatPage,
|
||||
name: 'chat',
|
||||
meta: { hidden: false }
|
||||
},
|
||||
{
|
||||
// 统一 /im 分组:下分 home(聊天壳)+ manager(Layout 管理壳)
|
||||
path: '/im',
|
||||
name: 'Im',
|
||||
redirect: '/im/home/message',
|
||||
meta: { hidden: false, title: 'IM 即时通讯' },
|
||||
children: [
|
||||
{
|
||||
// 聊天壳:全屏沉浸式应用,带 ToolBar + keep-alive
|
||||
// hidden:true 不在 yudao 侧边栏菜单显示;三个子 Tab 通过内部 ToolBar 切换
|
||||
path: 'home',
|
||||
component: () => import('@/views/im/home/Index.vue'),
|
||||
name: 'ImHome',
|
||||
redirect: '/im/home/message',
|
||||
meta: { hidden: true, title: '聊天' },
|
||||
children: [
|
||||
{
|
||||
path: 'message',
|
||||
component: () => import('@/views/im/home/pages/MessagePage.vue'),
|
||||
name: 'ImHomeMessage',
|
||||
meta: { hidden: true, title: '消息' }
|
||||
},
|
||||
{
|
||||
path: 'friend',
|
||||
component: () => import('@/views/im/home/pages/FriendPage.vue'),
|
||||
name: 'ImHomeFriend',
|
||||
meta: { hidden: true, title: '好友' }
|
||||
},
|
||||
{
|
||||
path: 'group',
|
||||
component: () => import('@/views/im/home/pages/GroupPage.vue'),
|
||||
name: 'ImHomeGroup',
|
||||
meta: { hidden: true, title: '群聊' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'manager/message',
|
||||
component: Layout,
|
||||
name: 'ImManagerMessage',
|
||||
redirect: '/im/manager/message/index',
|
||||
meta: { hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/im/manager/message/index.vue'),
|
||||
name: 'ImManagerMessageIndex',
|
||||
meta: {
|
||||
canTo: true,
|
||||
hidden: false,
|
||||
noTagsView: false,
|
||||
icon: 'ep:chat-dot-round',
|
||||
title: '消息管理'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'manager/friend',
|
||||
component: Layout,
|
||||
name: 'ImManagerFriend',
|
||||
redirect: '/im/manager/friend/index',
|
||||
meta: { hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/im/manager/friend/index.vue'),
|
||||
name: 'ImManagerFriendIndex',
|
||||
meta: {
|
||||
canTo: true,
|
||||
hidden: false,
|
||||
noTagsView: false,
|
||||
icon: 'ep:user',
|
||||
title: '好友管理'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'manager/group',
|
||||
component: Layout,
|
||||
name: 'ImManagerGroup',
|
||||
redirect: '/im/manager/group/index',
|
||||
meta: { hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/im/manager/group/index.vue'),
|
||||
name: 'ImManagerGroupIndex',
|
||||
meta: {
|
||||
canTo: true,
|
||||
hidden: false,
|
||||
noTagsView: false,
|
||||
icon: 'ep:user-filled',
|
||||
title: '群管理'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- TODO @芋艿:这个只是临时的 -->
|
||||
<div class="im-manager-placeholder">我是好友管理测试界面</div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'ImManagerFriend' })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.im-manager-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
font-size: 18px;
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- TODO @芋艿:这个只是临时的 -->
|
||||
<div class="im-manager-placeholder">我是群管理测试界面</div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'ImManagerGroup' })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.im-manager-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
font-size: 18px;
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- TODO @芋艿:这个只是临时的 -->
|
||||
<div class="im-manager-placeholder">我是消息管理测试界面</div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'ImManagerMessage' })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.im-manager-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
font-size: 18px;
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue