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 () => {
return openDB(DB_NAME, 3, {
return openDB(DB_NAME, 4, {
upgrade(db) {
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME); // 使用 userId 作为 key

View File

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

View File

@@ -37,7 +37,7 @@
<el-button class="groupInfoBtn" style="right: 200px;" v-if="selected"
@click="groupManagementVisible = true">群管理</el-button>
<el-button class="groupInfoBtn" style="right: 110px;" v-if="selected"
@click="leaveGroup">离开群聊</el-button>
@click="leavegroup">离开群聊</el-button>
</div>
</el-col>
</el-row>
@@ -47,14 +47,14 @@
<el-scrollbar ref="scrollbarRef" class="messagebox" style="height: 450px;">
<div v-for="(item) in messagebox" class="message-item">
<div
:class="{ 'message-item-profile': true, 'left': item.sender !== userinfo.user.u_id, 'right': item.sender === userinfo.user.u_id }">
<img :src="getAvatar(item.sender)" alt="User Avatar" />
:class="{ 'message-item-profile': true, 'left': item.from !== userinfo.user.id, 'right': item.from === userinfo.user.id }">
<img :src="getAvatar(item.from)" alt="User Avatar" />
</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>
<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>
</div>
</el-scrollbar>
@@ -75,7 +75,7 @@
<el-dialog v-model="createDialogVisible" title="创建群聊">
<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>
</el-dialog>
@@ -112,19 +112,19 @@
<el-table-column prop="m_avatar" label="" width="100">
<template #default="scope">
<!-- 使用 el-avatar 组件显示头像 -->
<el-avatar :src="memberSearchResult[scope.$index].m_avatar" size="large" />
<el-avatar :src="memberSearchResult[scope.$index].u_avatar" size="large" />
</template>
</el-table-column>
<el-table-column prop="m_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_name" label="姓名" width="125"></el-table-column>
<el-table-column prop="u_id" label="u_id" width="125"></el-table-column>
<el-table-column>
<template #default="scope">
<el-button @click="addFriend(scope.row.m_id)">添加好友</el-button>
<el-button @click="addFriend(scope.row.id)">添加好友</el-button>
</template>
</el-table-column>
<el-table-column>
<template #default="scope">
<el-button @click="kick(scope.row.m_id)">踢出</el-button>
<el-button @click="kick(scope.row.id)">踢出</el-button>
</template>
</el-table-column>
@@ -137,7 +137,7 @@
<el-row>
<el-col :span="24">
<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-row>
<el-row>
@@ -153,7 +153,7 @@
<el-table-column prop="identify" label="是否需要验证" width="125"></el-table-column>
<el-table-column>
<template #default="scope">
<el-button @click="joinGroup(scope.row.g_id)">加入</el-button>
<el-button @click="joingroup(scope.row.g_id)">加入</el-button>
</template>
</el-table-column>
</el-table>
@@ -162,14 +162,13 @@
</el-dialog>
</template>
<script setup>
import { groupMessageStore } from '@/store/message'
import { groupMessageStore } from '@/store/group_message'
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 { ElMessage } from 'element-plus'
import { onlineSocketStore } from '@/store/Online'
import axios from 'axios'
import { getGroups,createGroup, searchGroups, joinGroup, leaveGroup, getGroupMembers } from '@/api/group'
const socket = onlineSocketStore();
const userinfo = userInfoStore();
@@ -201,26 +200,8 @@ const memberSearchResult = computed(() => {
})
const groupsMember = 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 groupsMember = ref([]);
const groups = ref([])
const messagebox = ref([])
@@ -249,35 +230,25 @@ const scrollToBottom = () => {
});
};
const getGroupMembers = async (g_id) => {
axios({
url: '/api/group/getmember/' + g_id,
headers: {
'Authorization': userinfo.token
}
}).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 getgroupmembers = async (g_id) => {
try{
groupsMember.value = await getGroupMembers(g_id)
}catch(error){
ElMessage.error("获取群成员失败:",error)
}
}
const switchGroup = async (g_id, g_name, g_avatar, g_note) => {
groupMessage.reset()
groupMessage.g_id = g_id
inputDisabled.value = false
getGroupMembers(g_id)
await getgroupmembers(g_id)
if (selectedGroupId.value) {
groupMessage.saveMessagesHistory(userinfo.user.u_id, selectedGroupId.value)
// groupMessage.saveMessagesHistory(userinfo.user.u_id, selectedGroupId.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()
messagebox.value = groupMessage.historymessages
selectedGroupId.value = g_id
@@ -293,29 +264,24 @@ const sendMessage = () => {
ElMessage.error('消息不能为空')
return
}
// 发送四类消息给目标用户
const msg = {
group: true,
message: true,
system: false,
sender: userinfo.user.u_id,
sender_name: userinfo.user.u_name,
target: selectedGroupId.value,
target_name: selectedGroupName.value,
cmd: 'GROUP_MESSAGE',
group: groupMessage.g_id,
from: userinfo.user.id,
to: 0,
content: message.value,
time: new Date().toLocaleString()
};
socket.send(msg)
groupMessage.addMessage(msg)
groupMessage.recieveMessage(userinfo.user.id, msg)
message.value = ''
scrollToBottom()
}
const getAvatar = (u_id) => {
// 假设 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
};
// 监听消息
@@ -325,107 +291,76 @@ watch(() => groupMessage.messages.length, (newLength) => {
scrollToBottom()
})
const getGroups = async () => {
const response = await axios.get('/api/group/getgroups', {
headers: {
'Authorization': userinfo.token
const creategroup = async () => {
try{
if (groupName.value === '') {
ElMessage.error('群聊名称不能为空')
return
}
})
if (response.data.code === 200) {
groups.value = response.data.data
}
else {
ElMessage.error(response.data.msg)
if(await createGroup(groupName.value)){
ElMessage.success('创建成功')
createDialogVisible.value = false
groups.value = await getGroups()
}
}catch(error){
ElMessage.error('创建失败:',error)
}
}
const createGroup = async () => {
if (groupName.value === '') {
ElMessage.error('群聊名称不能为空')
return
}
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 = async () => {
try{
groupSearchResult.value = await searchGroups(searchGroup.value)
}catch(error){
ElMessage.error("搜索群聊失败:",error)
}
}
const searchGroups = () => {
axios({
url: '/api/group/search',
method: 'get',
headers: {
'Authorization': userinfo.token
},
params: {
g_name: searchGroup.value
const joingroup = async (g_id) => {
try{
if(!g_id){
ElMessage.error('群聊id不能为空')
return
}
}).then((response) => {
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) {
if(await joinGroup(g_id)){
ElMessage.success('加入成功')
joinDialogVisible.value = false
} else {
ElMessage.error(response.data.msg)
}
})
getGroups()
groups.value = await getGroups()
}
}catch(error){
ElMessage.error('加入群聊失败:',error)
}
}
const leaveGroup = async () => {
const response = await axios.get('/api/group/leave', {
params: {
g_id: selectedGroupId.value
const leavegroup = async () => {
try{
if(selectedGroupId.value===''){
ElMessage.error('未选择群聊')
return
}
}, {
headers: {
'Authorization': userinfo.token
}
})
if (response.data.code === 200) {
ElMessage.success('离开成功')
selected.value = false
selectedGroupId.value = ''
selectedGroupName.valuemessa = '请选择群聊'
messagebox.value = []
groupMessage.clearMessages()
if(await leaveGroup(selectedGroupId.value)){
ElMessage.success('离开成功')
selected.value = false
selectedGroupId.value = ''
selectedGroupName.value = '请选择群聊'
messagebox.value = []
groupMessage.reset()
groups.value = await getGroups()
}
}catch(error){
ElMessage.error('离开群聊失败:',error)
}
}
const setNote = () => {
}
onMounted(() => {
getGroups()
onMounted(async() => {
// getGroups()
groups.value = await getGroups()
})
</script>

View File

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

View File

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