reafactor: group basic function refactor complete

This commit is contained in:
merlin
2025-12-17 18:03:16 +08:00
parent a4bed485a5
commit 27c4a247d3
9 changed files with 267 additions and 155 deletions

93
src/api/group.ts Normal file
View File

@@ -0,0 +1,93 @@
import axios from "axios";
import { userInfoStore } from "@/store/user";
const userinfo = userInfoStore();
export const getGroups = async ()=> {
const response = await axios.get('/api/group/get',{
headers:{
'Authorization' : `Bearer ${userinfo.token}`
}
});
if(response.data.code === "200"){
return response.data.data;
} else {
throw new Error(response.data.message);
}
}
export const createGroup = async (group_name: string) => {
const response = await axios.post('/api/group/create',{
g_name: group_name
}
,{
headers:{
'Authorization' : `Bearer ${userinfo.token}`
}
});
if(response.data.code === "200"){
return true;
} else {
throw new Error(response.data.message);
}
}
export const searchGroups = async (group_name: string) => {
const response = await axios.post('/api/group/search',{
g_name: group_name
},{
headers:{
'Authorization' : `Bearer ${userinfo.token}`
}
})
if(response.data.code === "200"){
return response.data.data.records;
}else{
throw new Error(response.data.message);
}
}
export const joinGroup = async (group_id: number) => {
const response = await axios.get('/api/group/join/'+group_id,{
headers:{
'Authorization' : `Bearer ${userinfo.token}`
}})
if(response.data.code === "200"){
return true;
}else{
throw new Error(response.data.message);
}
}
export const leaveGroup = async (group_id: number) => {
const response = await axios.get('/api/group/leave/'+group_id,{
headers:{
'Authorization' : `Bearer ${userinfo.token}`
}})
if(response.data.code === "200"){
return true;
}else{
throw new Error(response.data.message);
}
}
export const getGroupMembers = async (group_id: number) => {
const response = await axios.get('/api/group/member/'+group_id,{
headers:{
'Authorization' : `Bearer ${userinfo.token}`
},
params:{
currentPage: 1,
pageSize: 50
}
})
if(response.data.code === "200"){
return response.data.data;
}else{
throw new Error(response.data.message);
}
}

View File

@@ -5,7 +5,7 @@ const STORE_NAME = 'groupHistoryMessages';
// 打开数据库 // 打开数据库
const getDB = async () => { const getDB = async () => {
return openDB(DB_NAME, 3, { return openDB(DB_NAME, 4, {
upgrade(db) { upgrade(db) {
if (!db.objectStoreNames.contains(STORE_NAME)) { if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME); // 使用 userId 作为 key db.createObjectStore(STORE_NAME); // 使用 userId 作为 key

View File

@@ -79,7 +79,7 @@ const router = createRouter({
{ {
path: '/room', path: '/room',
name: 'room', name: 'room',
component: () => import('../views/room/room.vue'), component: () => import('../views/room/index.vue'),
} }

View File

@@ -0,0 +1,76 @@
import { defineStore } from "pinia";
import {
saveGroupMessages,
loadGroupMessages,
deleteGroupMessages,
} from "@/functions/groupHistoryMessage";
interface Group_Message{
cmd: string;
from: number;
to: number;
group: number;
content: string;
time: string;
}
export const groupMessageStore = defineStore("groupMessageStore", {
state: () => ({
g_id: "",
historymessages: [],
messages: [],
corresponding: [],
}),
actions: {
async recieveMessage(u_id: number, message: Group_Message) {
const key = `${u_id}-${message.group}`
if(this.g_id !== '' && this.g_id === message.group) this.messages.push(message);
await saveGroupMessages(key, [message]);
},
reset(){
this.g_id = "";
this.historymessages = [];
this.messages = [];
this.corresponding = [];
},
addMessage(message: Group_Message) {
this.messages.push(message);
},
clearMessages() {
this.messages = [];
},
async initMessages() {
this.historymessages = [...this.historymessages, ...this.messages];
this.messages = [];
},
async getHistoryMessages(u_id:number, g_id: number) {
this.g_id = g_id;
const key = `${u_id}-${g_id}`;
try {
this.historymessages = await loadGroupMessages(key);
// 确保历史消息是数组类型
if (!Array.isArray(this.historymessages)) {
console.error("历史消息数据无效:", this.historymessages);
this.historymessages = []; // 如果数据无效,设置为空数组
}
} catch (error) {
console.log("加载历史消息时出错" + error);
}
},
async saveMessagesHistory(u_id: number, g_id: number) {
// const key = `${u_id}-${g_id}`;
// const messages = toRaw(this.messages);
this.messages = [];
this.historymessages = [];
// await saveGroupMessages(key, messages);
},
async deleteMessagesHistory(u_id: number, g_id: number) {
const key = `${u_id}-${g_id}`;
this.historymessages = [];
this.messages = [];
await deleteGroupMessages(key, []);
},
},
});

View File

@@ -1,11 +1,12 @@
<template> <template>
<div class="home-default"> <div class="home-default">
<h1>欢迎使用Myplayer</h1> <h1>欢迎使用Myplayer</h1>
<h2>当前版本v0.2.0测试版</h2> <h2>当前版本v0.3.0测试版</h2>
<p>v0.1.0 websocket实时聊天实装</p> <p>v0.1.0 websocket实时聊天实装</p>
<p>v0.1.1 面板更新websocket重连机制增加</p> <p>v0.1.1 面板更新websocket重连机制增加</p>
<p>v0.1.2 修复bug群聊功能性实现webRTC点对点语言聊天实装</p> <p>v0.1.2 修复bug群聊功能性实现webRTC点对点语言聊天实装</p>
<p>v0.2.0 修复若干bug完善了部分ui和逻辑</p> <p>v0.2.0 修复若干bug完善了部分ui和逻辑</p>
<p>v0.3.0 重构了后端以及前端逻辑</p>
<p>预期开发计划1播放器相关开发 2ui重绘 3群聊功能完善......</p> <p>预期开发计划1播放器相关开发 2ui重绘 3群聊功能完善......</p>
<p>总之还有好多事慢慢写吧</p> <p>总之还有好多事慢慢写吧</p>
</div> </div>

View File

@@ -37,7 +37,7 @@
<el-button class="groupInfoBtn" style="right: 200px;" v-if="selected" <el-button class="groupInfoBtn" style="right: 200px;" v-if="selected"
@click="groupManagementVisible = true">群管理</el-button> @click="groupManagementVisible = true">群管理</el-button>
<el-button class="groupInfoBtn" style="right: 110px;" v-if="selected" <el-button class="groupInfoBtn" style="right: 110px;" v-if="selected"
@click="leaveGroup">离开群聊</el-button> @click="leavegroup">离开群聊</el-button>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
@@ -47,14 +47,14 @@
<el-scrollbar ref="scrollbarRef" class="messagebox" style="height: 450px;"> <el-scrollbar ref="scrollbarRef" class="messagebox" style="height: 450px;">
<div v-for="(item) in messagebox" class="message-item"> <div v-for="(item) in messagebox" class="message-item">
<div <div
:class="{ 'message-item-profile': true, 'left': item.sender !== userinfo.user.u_id, 'right': item.sender === userinfo.user.u_id }"> :class="{ 'message-item-profile': true, 'left': item.from !== userinfo.user.id, 'right': item.from === userinfo.user.id }">
<img :src="getAvatar(item.sender)" alt="User Avatar" /> <img :src="getAvatar(item.from)" alt="User Avatar" />
</div> </div>
<div <div
:class="{ 'message-item-content': true, 'left': item.sender !== userinfo.user.u_id, 'right': item.sender === userinfo.user.u_id }"> :class="{ 'message-item-content': true, 'left': item.from !== userinfo.user.id, 'right': item.from === userinfo.user.id }">
{{ item.content }}</div> {{ item.content }}</div>
<div <div
:class="{ 'message-item-time': true, 'left': item.sender === userinfo.user.u_id, 'right': item.sender !== userinfo.user.u_id }"> :class="{ 'message-item-time': true, 'left': item.from === userinfo.user.id, 'right': item.from !== userinfo.user.id }">
{{ item.time }}</div> {{ item.time }}</div>
</div> </div>
</el-scrollbar> </el-scrollbar>
@@ -75,7 +75,7 @@
<el-dialog v-model="createDialogVisible" title="创建群聊"> <el-dialog v-model="createDialogVisible" title="创建群聊">
<el-input v-model="groupName" placeholder="请输入群聊名称" clearable style="width: 80%;"></el-input> <el-input v-model="groupName" placeholder="请输入群聊名称" clearable style="width: 80%;"></el-input>
<el-button type="primary" @click="createGroup" <el-button type="primary" @click="creategroup"
style="position: absolute;bottom: 10px;right: 10px;">创建</el-button> style="position: absolute;bottom: 10px;right: 10px;">创建</el-button>
</el-dialog> </el-dialog>
@@ -112,19 +112,19 @@
<el-table-column prop="m_avatar" label="" width="100"> <el-table-column prop="m_avatar" label="" width="100">
<template #default="scope"> <template #default="scope">
<!-- 使用 el-avatar 组件显示头像 --> <!-- 使用 el-avatar 组件显示头像 -->
<el-avatar :src="memberSearchResult[scope.$index].m_avatar" size="large" /> <el-avatar :src="memberSearchResult[scope.$index].u_avatar" size="large" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="m_name" label="姓名" width="125"></el-table-column> <el-table-column prop="u_name" label="姓名" width="125"></el-table-column>
<el-table-column prop="m_id" label="id" width="125"></el-table-column> <el-table-column prop="u_id" label="u_id" width="125"></el-table-column>
<el-table-column> <el-table-column>
<template #default="scope"> <template #default="scope">
<el-button @click="addFriend(scope.row.m_id)">添加好友</el-button> <el-button @click="addFriend(scope.row.id)">添加好友</el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column> <el-table-column>
<template #default="scope"> <template #default="scope">
<el-button @click="kick(scope.row.m_id)">踢出</el-button> <el-button @click="kick(scope.row.id)">踢出</el-button>
</template> </template>
</el-table-column> </el-table-column>
@@ -137,7 +137,7 @@
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-input v-model="searchGroup" placeholder="请输入群聊名称" clearable style="width: 80%;" <el-input v-model="searchGroup" placeholder="请输入群聊名称" clearable style="width: 80%;"
:prefix-icon="Search" @keyup.enter="searchGroups"></el-input> :prefix-icon="Search" @keyup.enter="searchgroups"></el-input>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
@@ -153,7 +153,7 @@
<el-table-column prop="identify" label="是否需要验证" width="125"></el-table-column> <el-table-column prop="identify" label="是否需要验证" width="125"></el-table-column>
<el-table-column> <el-table-column>
<template #default="scope"> <template #default="scope">
<el-button @click="joinGroup(scope.row.g_id)">加入</el-button> <el-button @click="joingroup(scope.row.g_id)">加入</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -162,14 +162,13 @@
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { groupMessageStore } from '@/store/message' import { groupMessageStore } from '@/store/group_message'
import { onMounted, ref, computed, nextTick, watch } from 'vue' import { onMounted, ref, computed, nextTick, watch } from 'vue'
import { userInfoStore } from '@/store/store' import { userInfoStore } from '@/store/user'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { onlineSocketStore } from '@/store/Online' import { onlineSocketStore } from '@/store/Online'
import axios from 'axios' import { getGroups,createGroup, searchGroups, joinGroup, leaveGroup, getGroupMembers } from '@/api/group'
const socket = onlineSocketStore(); const socket = onlineSocketStore();
const userinfo = userInfoStore(); const userinfo = userInfoStore();
@@ -201,26 +200,8 @@ const memberSearchResult = computed(() => {
}) })
const groupsMember = ref([ const groupsMember = ref([]);
const groups = ref([])
]);
const groups = ref([
// {
// g_id: '1',
// g_name: '我的群',
// g_avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif'
// },
// {
// g_id: '2',
// g_name: '我的好友群',
// g_avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif'
// },
// {
// g_id: '3',
// g_name: '我的家人群',
// g_avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif'
// },
])
const messagebox = ref([]) const messagebox = ref([])
@@ -249,35 +230,25 @@ const scrollToBottom = () => {
}); });
}; };
const getGroupMembers = async (g_id) => { const getgroupmembers = async (g_id) => {
axios({ try{
url: '/api/group/getmember/' + g_id, groupsMember.value = await getGroupMembers(g_id)
headers: { }catch(error){
'Authorization': userinfo.token ElMessage.error("获取群成员失败:",error)
} }
}).then((response) => {
if (response.data.code === 200) {
groupsMember.value = response.data.data
currentRole.value = response.data.data.find(item => item.m_id === userinfo.user.u_id).role
}
else {
ElMessage.error(response.data.msg)
}
})
} }
const switchGroup = async (g_id, g_name, g_avatar, g_note) => { const switchGroup = async (g_id, g_name, g_avatar, g_note) => {
groupMessage.reset()
groupMessage.g_id = g_id groupMessage.g_id = g_id
inputDisabled.value = false inputDisabled.value = false
getGroupMembers(g_id) await getgroupmembers(g_id)
if (selectedGroupId.value) { if (selectedGroupId.value) {
groupMessage.saveMessagesHistory(userinfo.user.u_id, selectedGroupId.value) // groupMessage.saveMessagesHistory(userinfo.user.u_id, selectedGroupId.value)
messagebox.value = [] messagebox.value = []
console.log(messagebox.value)
} }
await groupMessage.getHistoryMessages(userinfo.user.u_id, g_id) await groupMessage.getHistoryMessages(userinfo.user.id, g_id)
await groupMessage.initMessages() await groupMessage.initMessages()
messagebox.value = groupMessage.historymessages messagebox.value = groupMessage.historymessages
selectedGroupId.value = g_id selectedGroupId.value = g_id
@@ -293,29 +264,24 @@ const sendMessage = () => {
ElMessage.error('消息不能为空') ElMessage.error('消息不能为空')
return return
} }
// 发送四类消息给目标用户
const msg = { const msg = {
group: true, cmd: 'GROUP_MESSAGE',
message: true, group: groupMessage.g_id,
system: false, from: userinfo.user.id,
sender: userinfo.user.u_id, to: 0,
sender_name: userinfo.user.u_name,
target: selectedGroupId.value,
target_name: selectedGroupName.value,
content: message.value, content: message.value,
time: new Date().toLocaleString() time: new Date().toLocaleString()
}; };
socket.send(msg) socket.send(msg)
groupMessage.addMessage(msg) groupMessage.recieveMessage(userinfo.user.id, msg)
message.value = '' message.value = ''
scrollToBottom() scrollToBottom()
} }
const getAvatar = (u_id) => { const getAvatar = (u_id) => {
// 假设 groupsMember 是一个响应式变量,包含多个对象 // 假设 groupsMember 是一个响应式变量,包含多个对象
const member = groupsMember.value.find(member => member.u_id === u_id); const member = groupsMember.value.find(member => member.id === u_id);
return member ? member.u_avatar : null; // 如果找到匹配项,返回 u_avatar否则返回 null return member ? member.u_avatar : null; // 如果找到匹配项,返回 u_avatar否则返回 null
}; };
// 监听消息 // 监听消息
@@ -325,107 +291,76 @@ watch(() => groupMessage.messages.length, (newLength) => {
scrollToBottom() scrollToBottom()
}) })
const getGroups = async () => {
const response = await axios.get('/api/group/getgroups', { const creategroup = async () => {
headers: { try{
'Authorization': userinfo.token if (groupName.value === '') {
ElMessage.error('群聊名称不能为空')
return
} }
}) if(await createGroup(groupName.value)){
if (response.data.code === 200) { ElMessage.success('创建成功')
groups.value = response.data.data createDialogVisible.value = false
} groups.value = await getGroups()
else { }
ElMessage.error(response.data.msg) }catch(error){
ElMessage.error('创建失败:',error)
} }
} }
const createGroup = async () => { const searchgroups = async () => {
if (groupName.value === '') { try{
ElMessage.error('群聊名称不能为空') groupSearchResult.value = await searchGroups(searchGroup.value)
return }catch(error){
} ElMessage.error("搜索群聊失败:",error)
const response = await axios.post('/api/group/create', {
g_name: groupName.value
}, {
headers: {
'Authorization': userinfo.token
}
})
if (response.data.code === 200) {
ElMessage.success('创建成功')
createDialogVisible.value = false
getGroups()
} }
} }
const searchGroups = () => { const joingroup = async (g_id) => {
axios({ try{
url: '/api/group/search', if(!g_id){
method: 'get', ElMessage.error('群聊id不能为空')
headers: { return
'Authorization': userinfo.token
},
params: {
g_name: searchGroup.value
} }
}).then((response) => { if(await joinGroup(g_id)){
if (response.data.code === 200) {
groupSearchResult.value = response.data.data
} else {
ElMessage.error(response.data.msg)
}
})
}
const joinGroup = (g_id) => {
axios({
url: '/api/group/joingroup',
method: 'post',
data: {
g_id: g_id,
u_id: userinfo.user.u_id,
identify: 0
},
headers: {
'Authorization': userinfo.token
}
}).then((response) => {
if (response.data.code === 200) {
ElMessage.success('加入成功') ElMessage.success('加入成功')
joinDialogVisible.value = false joinDialogVisible.value = false
} else { groups.value = await getGroups()
ElMessage.error(response.data.msg)
} }
}) }catch(error){
getGroups() ElMessage.error('加入群聊失败:',error)
}
} }
const leaveGroup = async () => { const leavegroup = async () => {
const response = await axios.get('/api/group/leave', { try{
params: { if(selectedGroupId.value===''){
g_id: selectedGroupId.value ElMessage.error('未选择群聊')
return
} }
}, { if(await leaveGroup(selectedGroupId.value)){
headers: { ElMessage.success('离开成功')
'Authorization': userinfo.token selected.value = false
selectedGroupId.value = ''
selectedGroupName.value = '请选择群聊'
messagebox.value = []
groupMessage.reset()
groups.value = await getGroups()
} }
}) }catch(error){
if (response.data.code === 200) { ElMessage.error('离开群聊失败:',error)
ElMessage.success('离开成功')
selected.value = false
selectedGroupId.value = ''
selectedGroupName.valuemessa = '请选择群聊'
messagebox.value = []
groupMessage.clearMessages()
} }
} }
const setNote = () => { const setNote = () => {
} }
onMounted(() => { onMounted(async() => {
getGroups() // getGroups()
groups.value = await getGroups()
}) })
</script> </script>

View File

@@ -1,15 +1,16 @@
import { ref } from "vue"; import { ref } from "vue";
import { userInfoStore } from "@/store/store"; import { userInfoStore } from "@/store/user";
import { messageStore } from "@/store/message"; import { messageStore } from "@/store/message";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { messageSignStore } from "@/store/message_sign"; import { messageSignStore } from "@/store/message_sign";
import { groupMessageStore } from "@/store/group_message";
// userinfo 实例 // userinfo 实例
const userinfo = userInfoStore(); const userinfo = userInfoStore();
// message 实例 // message 实例
const message = messageStore(); const message = messageStore();
// groupMessage 实例 // groupMessage 实例
// const groupMessage = groupMessageStore(); const groupMessage = groupMessageStore();
// messageSignStoregn 实例 // messageSignStoregn 实例
// const messageSign = messageSignStore(); // const messageSign = messageSignStore();
@@ -85,6 +86,7 @@ export const connectWebSocket = () => {
switch(cmd){ switch(cmd){
case "MESSAGE": case "MESSAGE":
message.addMessage(MessageData); message.addMessage(MessageData);
// TODO需要使用u_name或者u_id进行消息标记
// messageSign.setMessageSign(true); // messageSign.setMessageSign(true);
// ElMessage.info("您有一条新的消息"); // ElMessage.info("您有一条新的消息");
break; break;
@@ -92,6 +94,11 @@ export const connectWebSocket = () => {
messageSign.setMessageSign(true); messageSign.setMessageSign(true);
ElMessage.info("您有一条新的邀请消息"); ElMessage.info("您有一条新的邀请消息");
break; break;
case "GROUP_MESSAGE":
groupMessage.recieveMessage(userinfo.user.id, MessageData);
// TODO: 需要使用groupId进行消息标记
break;
} }
}catch(error){ }catch(error){
console.error("解析 JSON 失败:", error); console.error("解析 JSON 失败:", error);

View File

@@ -1,5 +1,5 @@
import { ref } from "vue"; import { ref } from "vue";
import { userInfoStore } from "@/store/store"; import { userInfoStore } from "@/store/user";
import { onCallStore } from "@/store/VoiceTarget"; import { onCallStore } from "@/store/VoiceTarget";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";