前端:
1、会话列表、消息界面展示与后端打通 后端: 1、修复会话和消息服务现存问题 2、会话对象不再返回Message对象,而是使用MessageBaseDto替代 3、修改查询会话列表和会话信息的逻辑 4、新增消息列表查询 文档: 后端代码规范文档新增从数据库同步到模型的命令
This commit is contained in:
@@ -11,7 +11,10 @@ const routes = [
|
||||
{
|
||||
path: '/',
|
||||
component: MainView,
|
||||
|
||||
redirect: '/messages',
|
||||
meta: {
|
||||
requiresAuth: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/messages',
|
||||
@@ -42,6 +45,7 @@ const routes = [
|
||||
{
|
||||
path: '/index',
|
||||
component: MainView,
|
||||
redirect: '/messages',
|
||||
meta: {
|
||||
requiresAuth: true
|
||||
}
|
||||
@@ -56,10 +60,18 @@ const router = createRouter({
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const authStore = useAuthStore();
|
||||
if (to.path == '/auth/login') {
|
||||
if (authStore.isLoggedIn) {
|
||||
message.info('已登录,即将跳转...');
|
||||
next('/');
|
||||
}
|
||||
next();
|
||||
}
|
||||
if (to.meta.requiresAuth && !authStore.isLoggedIn) {
|
||||
message.info('未登录,即将跳转...');
|
||||
next('auth/login');
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -11,5 +11,25 @@ export const messageService = {
|
||||
* 清空所有会话消息
|
||||
* @returns
|
||||
*/
|
||||
clearConversation: () => request.post('')
|
||||
clearConversation: () => request.post(''),
|
||||
/**
|
||||
* 获取单个会话信息
|
||||
* @param {*} conversationId
|
||||
* @returns
|
||||
*/
|
||||
getConversationById: (conversationId) => request.get(`/conversation/get?conversationId=${conversationId}`),
|
||||
/**
|
||||
* 获取历史消息列表
|
||||
* @param {*} conversationId 指定会话
|
||||
* @param {*} msgId
|
||||
* @param {*} pageSize
|
||||
* @returns
|
||||
*/
|
||||
getHistoryMessages: (conversationId, msgId, pageSize = 10) => request.get(`/message/getmessageList?conversationId=${conversationId}&msgId=${msgId}&pageSize=${pageSize}`),
|
||||
/**
|
||||
* 获取最新消息
|
||||
* @param {*} conversationId
|
||||
* @returns
|
||||
*/
|
||||
getMessages: (conversationId) => request.get(`/message/getmessageList?conversationId=${conversationId}`)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export function getChatCodeStr(code) {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return '私聊'
|
||||
case 1:
|
||||
return '群聊'
|
||||
default:
|
||||
return '未知类型'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export function formatDate(dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // 补零
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
const nowDate = new Date();
|
||||
if (year == nowDate.getFullYear() && month == String(nowDate.getMonth() + 1).padStart(2, '0') && day == String(nowDate.getDate()).padStart(2, '0')) {
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
if (year == nowDate.getFullYear()) {
|
||||
return `${month}/${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
@@ -126,7 +126,6 @@ function handleGoToChat() {
|
||||
const loadContactList = async (page = 1,limit = 100) => {
|
||||
const res = await friendService.getFriendList(page,limit);
|
||||
contacts.value = res.data;
|
||||
console.log(contacts.value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<section class="chat-panel">
|
||||
<header class="chat-header">
|
||||
<span class="title">{{ currentSession?.name || '未选择会话' }}</span>
|
||||
<span class="title">{{ conversationInfo?.targetName || '未选择会话' }}</span>
|
||||
<div class="actions">
|
||||
<button @click="startCall('video')">📹</button>
|
||||
<button @click="startCall('voice')">📞</button>
|
||||
@@ -9,15 +9,15 @@
|
||||
</header>
|
||||
|
||||
<div class="chat-history" ref="historyRef">
|
||||
<div v-for="m in activeMessages" :key="m.id" :class="['msg', m.mine ? 'mine' : 'other']">
|
||||
<img :src="m.mine ? (myInfo?.avatar || defaultAvatar) : currentSession?.avatar" class="avatar-chat" />
|
||||
<div v-for="m in messages" :key="m.id" :class="['msg', m.senderId == myInfo.id ? 'mine' : 'other']">
|
||||
<img :src="m.senderId == myInfo.id ? (myInfo?.avatar || defaultAvatar) : defaultAvatar" class="avatar-chat" />
|
||||
|
||||
<div class="msg-content">
|
||||
<div class="bubble">
|
||||
<div v-if="m.type === 'text'">{{ m.content }}</div>
|
||||
<div v-if="m.type === 'Text'">{{ m.content }}</div>
|
||||
<div v-else-if="m.type === 'emoji'" class="emoji-msg">{{ m.content }}</div>
|
||||
</div>
|
||||
<span class="msg-time">{{ m.time }}</span>
|
||||
<span class="msg-time">{{ formatDate(m.timeStamp) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,6 +45,8 @@
|
||||
import { ref, computed, nextTick, onMounted } from 'vue';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import defaultAvatar from '@/assets/default_avatar.png';
|
||||
import { messageService } from '@/services/message';
|
||||
import { formatDate } from '@/utils/formatDate';
|
||||
|
||||
const props = defineProps({
|
||||
id:{
|
||||
@@ -56,6 +58,8 @@ const input = ref(''); // 输入框内容
|
||||
const historyRef = ref(null); // 绑定 DOM 用于滚动
|
||||
const myInfo = useAuthStore().userInfo;
|
||||
|
||||
const conversationInfo = ref(null)
|
||||
|
||||
// --- 模拟会话数据 (实际开发中应从后端或 store 获取) ---
|
||||
const sessions = ref([
|
||||
{ id: 1, name: '南浔', avatar: 'https://i.pravatar.cc/40?1', last: '' },
|
||||
@@ -71,12 +75,6 @@ const messages = ref({
|
||||
2: []
|
||||
});
|
||||
|
||||
// --- 计算属性 ---
|
||||
const currentSession = computed(() => sessions.value.find(s => s.id == props.id));
|
||||
const activeMessages = computed(() => messages.value[props.id] || []);
|
||||
|
||||
// --- 功能函数 ---
|
||||
|
||||
// 自动滚动到底部
|
||||
const scrollToBottom = async () => {
|
||||
await nextTick(); // 等待 DOM 更新后执行
|
||||
@@ -128,8 +126,21 @@ function toggleEmoji() {
|
||||
console.log('打开表情面板');
|
||||
}
|
||||
|
||||
async function loadConversation(conversationId) {
|
||||
const res = await messageService.getConversationById(conversationId);
|
||||
conversationInfo.value = res.data;
|
||||
console.log(res)
|
||||
}
|
||||
|
||||
async function loadMessages(conversationId, msgId = null, pageSize = null) {
|
||||
const res = await messageService.getMessages(conversationId);
|
||||
messages.value = res.data;
|
||||
}
|
||||
|
||||
// 初始化时滚动到底部
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
await loadConversation(props.id);
|
||||
await loadMessages(props.id);
|
||||
scrollToBottom();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
<div v-for="s in filteredSessions" :key="s.id"
|
||||
class="list-item" :class="{active: activeId === s.id}" @click="selectSession(s)">
|
||||
<div class="avatar-container">
|
||||
<img :src="s.avatar" class="avatar-std" />
|
||||
<span v-if="s.unread > 0" class="unread-badge">{{ s.unread }}</span>
|
||||
<img :src="s.targetAvatar ?? defaultAvatar" class="avatar-std" />
|
||||
<span v-if="s.unread > 0" class="unread-badge">{{ s.unreadCount ?? 0 }}</span>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="name-row">
|
||||
<span class="name">{{ s.name }}</span>
|
||||
<span class="time">{{ s.lastTime }}</span>
|
||||
<span class="name">{{ s.targetName ?? '未知用户' }}</span>
|
||||
<span class="time">{{ formatDate(s.dateTime) ?? '1970/1/1 00:00:00' }}</span>
|
||||
</div>
|
||||
<div class="last-msg">{{ s.last }}</div>
|
||||
<div class="last-msg">{{ s.lastMessage ?? '获取消息内容失败' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,18 +32,18 @@
|
||||
|
||||
<script setup>
|
||||
import router from '@/router'
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { ref, computed, nextTick, onMounted } from 'vue'
|
||||
import { messageService } from '@/services/message'
|
||||
import defaultAvatar from '@/assets/default_avatar.png'
|
||||
import { formatDate } from '@/utils/formatDate'
|
||||
|
||||
const searchQuery = ref('')
|
||||
const input = ref('')
|
||||
const historyRef = ref(null)
|
||||
const activeId = ref(1)
|
||||
const conversations = ref([]);
|
||||
|
||||
const sessions = ref([
|
||||
{ id: 1, name: '南浔', last: '在写代码呢', lastTime: '14:20', avatar: 'https://i.pravatar.cc/40?1', unread: 2 },
|
||||
{ id: 2, name: '技术群', last: '部署好了', lastTime: '12:05', avatar: 'https://i.pravatar.cc/40?2', unread: 0 }
|
||||
])
|
||||
|
||||
const filteredSessions = computed(() => sessions.value.filter(s => s.name.includes(searchQuery.value)))
|
||||
const filteredSessions = computed(() => conversations.value.filter(s => s.targetName.includes(searchQuery.value)))
|
||||
|
||||
const currentSession = computed(() => sessions.value.find(s => s.id === activeId.value))
|
||||
|
||||
@@ -59,6 +59,16 @@ const scrollToBottom = async () => {
|
||||
if (historyRef.value) historyRef.value.scrollTop = historyRef.value.scrollHeight
|
||||
}
|
||||
|
||||
async function loadConversation() {
|
||||
const res = await messageService.getConversations();
|
||||
conversations.value = res.data;
|
||||
console.log(res)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadConversation();
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user