refactor: friend basic function refactory complete

This commit is contained in:
merlin
2025-12-16 18:09:06 +08:00
parent b2bcfdf1a9
commit a4bed485a5
33 changed files with 1269 additions and 822 deletions

693
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,9 +9,9 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"axios": "^1.7.9", "axios": "^1.12.0",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dplayer": "^1.27.1", "dplayer": "^1.27.0",
"element-plus": "^2.9.4", "element-plus": "^2.9.4",
"express": "^4.21.2", "express": "^4.21.2",
"hls.js": "^1.5.20", "hls.js": "^1.5.20",

50
src/api/code.ts Normal file
View File

@@ -0,0 +1,50 @@
import axios from "axios";
import { codeStore } from "@/store/user";
export const sendCode = async (account: string) => {
const codeStoreInstance = codeStore();
try {
const response = await axios.post('/api/code/send', {
account: account,
}, {
headers: {
'Content-Type': 'application/json',
}
});
if (response.data.code === "200") {
codeStoreInstance.setID(response.data.data.c_id);
console.log(response.data);
return true;
} else {
alert('发送验证码失败!' + response.data.code);
return false;
}
} catch (error) {
console.error('Send code error:', error);
return false;
}
}
export const verifyCode = async (code: string) => {
const codeStoreInstance = codeStore();
try {
const response = await axios.post('/api/code/verify', {
c_id: codeStoreInstance.getID(),
code: code,
}, {
headers: {
'Content-Type': 'application/json',
}
});
if (response.data.code === "200") {
return true;
} else {
alert(response.data.code + response.data.message);
return false;
}
} catch (error) {
console.error('Verify code error:', error);
return false;
}
}

76
src/api/friend.ts Normal file
View File

@@ -0,0 +1,76 @@
import axios from "axios";
import { userInfoStore } from "@/store/user";
import { onlineSocketStore } from "@/store/Online";
const userinfo = userInfoStore();
const socket = onlineSocketStore()
export const getFriends = async () => {
const response = await axios.get('/api/friend/get', { headers: { 'Authorization': "Bearer " + userinfo.token },params: {
page: 1,
size: 5
} })
if (response.data.code === "200") {
return response.data.data
} else {
console.error(response.data.msg)
return
}
}
export const deleteFriend = async (u_id: number) => {
const response = await
axios.post('/api/friend/delete',{},
{
headers:{
'Authorization': "Bearer " + userinfo.token,
},params:{
f_id: u_id
}})
if (response.data.code === "200") {
return true
} else {
console.error(response.data.msg)
return false
}
}
export const searchFriend = async (inputValue: string) => {
const response = await axios.post('/api/user/search',{
u_name: inputValue
},{
headers:{
'Authorization': "Bearer " + userinfo.token
}
})
if(response.data.code === "200"){
return response.data.data.result
} else {
console.error(response.data.msg)
return []
}
};
export const addFriend = async (f_id: number) =>{
const response = await axios.post('/api/inviting/friends',{
id: f_id
},{
headers:{
'Authorization': "Bearer " + userinfo.token
}
})
if(response.data.code === "200"){
socket.send({
cmd: "PERSONAL_NOTIFY",
from: userinfo.user.id,
to: f_id,
content: "",
time: new Date().toISOString(),
});
}
else {
console.error(response.data.message)
}
return response.data
}

41
src/api/inviting.ts Normal file
View File

@@ -0,0 +1,41 @@
import axios from "axios";
import { userInfoStore } from "@/store/user";
const userinfo = userInfoStore();
interface inviting{
i_id: number;
inviter: number;
target: number;
status: number;
room: number;
time: string;
}
export const getInvitings = async () => {
const response = await axios.get('/api/inviting/get', { headers: { 'Authorization': "Bearer " + userinfo.token },params: {
currentPage: 1,
pageSize: 5
} })
if (response.data.code === "200") {
return response.data
} else {
console.error(response.data.msg)
return
}
}
export const handleInviting = async (inviting: inviting) => {
const response = await axios.post('/api/inviting/handle',inviting,{
headers:{
'Authorization': "Bearer " + userinfo.token
}
})
if (response.data.code === "200") {
} else {
console.error(response.data.message)
}
return response.data
}

64
src/api/login.ts Normal file
View File

@@ -0,0 +1,64 @@
import axios from 'axios';
import { codeStore, userInfoStore } from '@/store/user';
import router from '@/router';
const userinfo = userInfoStore();
const codeData = codeStore()
export const login = async (account:string, password:string) => {
try {
const response = await axios.post('/api/shadow/login', {
account: account,
password: password,
}, {
headers: {
'Content-Type': 'application/json',
}
});
if (response.data.code === "200") {
userinfo.token = response.data.data.token;
userinfo.user = response.data.data.userinfo;
userinfo.account = account;
console.log(response.data);
router.replace({ name: 'home' });
} else {
alert('账号或密码错误!' + response.data.code);
}
} catch (error) {
console.error('Login error:', error);
alert('Login failed. Please try again later.');
}
};
interface registerData{
code: string;
account: string;
password: string;
}
export const register = async (code:registerData) => {
try {
const response = await axios.post('/api/shadow/register', {
c_id: codeData.getID(),
code: code.code,
account: code.account,
password: code.password,
}, {
headers: {
'Content-Type': 'application/json',
}
});
if (response.data.code === "200") {
userinfo.token = response.data.data.token;
userinfo.user = response.data.data.userinfo;
userinfo.user.u_account = code.account;
console.log(response.data);
router.replace({ name: 'home' });
} else {
alert('注册失败!' + response.data.code + response.data.message);
}
} catch (error) {
console.error('Register error:', error);
alert('Register failed. Please try again later.');
}
};

21
src/api/room.ts Normal file
View File

@@ -0,0 +1,21 @@
import { userInfoStore } from "@/store/user";
import axios from "axios";
const userinfo = userInfoStore();
export const searchPlayRoom = async (inputValue: string) => {
const response = await axios.post('/api/playroom/search',{
r_name: inputValue
},{
headers:{
'Authorization': "Bearer " + userinfo.token
}
})
if(response.data.code === "200"){
return response.data.data
} else {
console.error(response.data.msg)
return []
}
}

24
src/api/user.ts Normal file
View File

@@ -0,0 +1,24 @@
import axios from 'axios'
import { userInfoStore } from '@/store/user'
const userinfo = userInfoStore()
export const getUserInfo = async () => {
console.log("getUserInfojs was used!")
try {
const response = await axios.get('/api/user/info', {
headers: {
'Authorization': 'Bearer ' + userinfo.token
}
})
if (response.data.code === "200") {
userinfo.user = response.data.data
return true
}
} catch (error) {
console.log(error)
return false
}
}

View File

@@ -4,7 +4,7 @@
class="global-message-button" class="global-message-button"
@click="readMessage" > @click="readMessage" >
<el-icon><Message /></el-icon> <el-icon><Message /></el-icon>
<div class="messagepoint" v-if="messagePoint.hasNewMessage"></div> <div class="messagepoint" v-if="messageSign.getMessageSign()"></div>
</el-button> </el-button>
</router-link> </router-link>
</template> </template>
@@ -12,11 +12,15 @@
<script setup> <script setup>
import { messagePointStore } from '@/store/store'; import { messagePointStore } from '@/store/store';
import { Message } from '@element-plus/icons-vue'; import { Message } from '@element-plus/icons-vue';
import { messageSignStore } from '@/store/message_sign';
const messagePoint = messagePointStore(); const messagePoint = messagePointStore();
const messageSign = messageSignStore();
const readMessage = () => { const readMessage = () => {
messagePoint.hasNewMessage = false; // messagePoint.hasNewMessage = false;
messageSign.setMessageSign(false);
} }

View File

@@ -29,8 +29,8 @@ import logo from '../assets/logo.png';
import { onlineSocketStore } from '@/store/Online'; import { onlineSocketStore } from '@/store/Online';
import router from '@/router'; import router from '@/router';
import { voiceStore } from '@/store/Voice'; import { voiceStore } from '@/store/Voice';
import { groupMessageStore } from '@/store/message'; import { groupMessageStore } from '@/store/message.ts';
import { userInfoStore } from '@/store/store'; import { userInfoStore } from '@/store/user';
const userInfo = userInfoStore() const userInfo = userInfoStore()
const voice = voiceStore() const voice = voiceStore()

View File

@@ -7,7 +7,7 @@ const STORE_NAME = 'historyMessages';
const initDB = async () => { const initDB = async () => {
try { try {
// 打开数据库,如果不存在则会创建一个新的数据库 // 打开数据库,如果不存在则会创建一个新的数据库
const db = await openDB('myplayer', 3, { const db = await openDB('myplayer', 4, {
// 数据库升级回调 // 数据库升级回调
upgrade(db) { upgrade(db) {
console.log('数据库升级中...'); console.log('数据库升级中...');
@@ -34,21 +34,21 @@ const initDB = async () => {
// 打开数据库 // 打开数据库
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); // 使用 id 作为 key
} }
}, },
}); });
}; };
// 存储消息(追加到历史记录中) // 存储消息(追加到历史记录中)
const saveMessages = async (userId, messages) => { const saveMessages = async (id, messages) => {
const db = await getDB(); const db = await getDB();
try { try {
// 读取现有的历史消息 // 读取现有的历史消息
const existingMessages = await loadMessages(userId); const existingMessages = await loadMessages(id);
// 确保 existingMessages 是一个数组 // 确保 existingMessages 是一个数组
const newMessages = Array.isArray(existingMessages) ? existingMessages : []; const newMessages = Array.isArray(existingMessages) ? existingMessages : [];
@@ -57,17 +57,17 @@ const saveMessages = async (userId, messages) => {
const updatedMessages = [...newMessages, ...messages]; // 将新消息追加到历史消息数组中 const updatedMessages = [...newMessages, ...messages]; // 将新消息追加到历史消息数组中
// 保存更新后的历史消息 // 保存更新后的历史消息
await db.put(STORE_NAME, updatedMessages, userId); await db.put(STORE_NAME, updatedMessages, id);
} catch (error) { } catch (error) {
console.error('Error saving messages:', error); console.error('Error saving messages:', error);
} }
}; };
// 获取消息 // 获取消息
const loadMessages = async (userId) => { const loadMessages = async (id) => {
const db = await getDB(); const db = await getDB();
try { try {
const messages = await db.get(STORE_NAME, userId); const messages = await db.get(STORE_NAME, id);
return messages || []; // 如果没有消息,返回空数组 return messages || []; // 如果没有消息,返回空数组
} catch (error) { } catch (error) {
console.error('Error loading messages:', error); console.error('Error loading messages:', error);
@@ -76,16 +76,16 @@ const loadMessages = async (userId) => {
}; };
// 删除消息 // 删除消息
const deleteMessages = async (userId,messages) => { const deleteMessages = async (id,messages) => {
const db = await getDB(); const db = await getDB();
try { try {
if(messages.length === 0){ if(messages.length === 0){
console.log('删除后没有消息了'); console.log('删除后没有消息了');
await db.delete(STORE_NAME, userId); await db.delete(STORE_NAME, id);
}else{ }else{
console.log('删除后还有消息') console.log('删除后还有消息')
console.log(messages) console.log(messages)
await db.put(STORE_NAME, messages, userId); await db.put(STORE_NAME, messages, id);
} }
} catch (error) { } catch (error) {
console.error('Error deleting messages:', error); console.error('Error deleting messages:', error);

View File

@@ -11,13 +11,13 @@ const router = createRouter({
{ {
path: '/home', path: '/home',
name: 'home', name: 'home',
component: () => import('../views/home/home.vue'), component: () => import('../views/home/index.vue'),
redirect: '/home/', redirect: '/home/',
children: [ children: [
{ {
path: '', path: '',
name: 'homeDefault', name: 'homeDefault',
component: () => import('../views/home/homeDefault.vue'), component: () => import('../views/home/default.vue'),
}, },
{ {
path: 'friends', path: 'friends',
@@ -32,7 +32,7 @@ const router = createRouter({
{ {
path: 'user', path: 'user',
name: 'User', name: 'User',
component: () => import('../views/user/user.vue'), component: () => import('../views/user/index.vue'),
}, },
{ {
path: 'search', path: 'search',

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from '@/socket/onlineSocket' import { connectWebSocket, disconnectWebSocket, sendMessage, setIsManualClose } from '@/websocket/onlineSocket'
import { messageStore } from './message' import { messageStore } from './message'
const message = messageStore() const message = messageStore()
@@ -8,17 +8,17 @@ export const onlineSocketStore = defineStore('onlineSocket', {
state: () => ({ state: () => ({
isConnected: false, isConnected: false,
hasGotMessage: false, hasGotMessage: false,
u_id: '' id: ''
}), }),
actions: { actions: {
connect(u_id) { connect(id) {
this.u_id = u_id; this.id = id;
if (this.isConnected === true) return if (this.isConnected === true) return
connectWebSocket(); connectWebSocket();
this.isConnected = true; this.isConnected = true;
if (!this.hasGotMessage) { if (!this.hasGotMessage) {
message.loadMessagesHistory(this.u_id) message.loadMessagesHistory(this.id)
this.hasGotMessage = true this.hasGotMessage = true
} }
}, },
@@ -27,11 +27,11 @@ export const onlineSocketStore = defineStore('onlineSocket', {
disconnectWebSocket(); disconnectWebSocket();
this.isConnected = false; this.isConnected = false;
if (this.hasGotMessage) { if (this.hasGotMessage) {
message.saveMessagesHistory(this.u_id) message.saveMessagesHistory(this.id)
} }
}, },
send(message) { send(message) {
sendMessage(message); sendMessage(JSON.stringify(message));
} }
} }
}) })

View File

@@ -1,4 +1,4 @@
import { connectVoicesocket, disconnectVoicesocket, sendMessage, hangup } from "@/socket/voiceSocket"; import { connectVoicesocket, disconnectVoicesocket, sendMessage, hangup } from "@/websocket/voiceSocket";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
export const voiceStore = defineStore("voice", { export const voiceStore = defineStore("voice", {

194
src/store/message.ts Normal file
View File

@@ -0,0 +1,194 @@
import { defineStore } from "pinia";
import {
saveMessages,
loadMessages,
deleteMessages,
} from "@/functions/historyMessages";
import {
saveGroupMessages,
loadGroupMessages,
deleteGroupMessages,
} from "@/functions/groupHistoryMessage";
import { toRaw } from "vue";
interface Message {
cmd: string;
from: number;
to: number;
content: string;
time: string;
}
export const messageStore = defineStore("messageStore", {
// 定义一个响应式数组来存储聊天消息
state: () => ({
historymessages: [],
messages: [],
from: 0,
to: 0,
corresponding: [],
}),
// 定义操作消息的函数
actions: {
// 添加消息到数组
addMessage(message: Message) {
this.messages.push(message);
},
// 清空所有消息
clearMessages() {
this.messages = [];
},
setCorresponding() {
// 过滤出当前聊天中的消息
this.corresponding = this.messages.filter(
(msg: Message) =>
(msg.from === this.from && msg.to === this.to) ||
(msg.from === this.to && msg.to === this.from)
);
const historymessages = this.historymessages.filter(
(msg: Message) =>
(msg.from === this.from && msg.to === this.to) ||
(msg.from === this.to && msg.to === this.from)
);
this.corresponding = [...historymessages, ...this.corresponding];
},
// 清除当前登录的聊天数据,保存到本地
async saveMessagesHistory(id: number) {
const messages = toRaw(this.messages);
await saveMessages(id, messages);
},
// 加载本地聊天数据
async loadMessagesHistory(id: number) {
try {
this.historymessages = await loadMessages(id);
// 确保历史消息是数组类型
if (!Array.isArray(this.historymessages)) {
console.error("历史消息数据无效:", this.historymessages);
this.historymessages = []; // 如果数据无效,设置为空数组
}
} catch (error) {
console.log("加载历史消息时出错" + error);
}
},
async deleteMessagesHistory(id: number, f_id: number) {
this.historymessages = this.historymessages.filter(
(msg: Message) =>
!(
(msg.from === f_id && msg.to === id) ||
(msg.from === id && msg.to === f_id)
)
);
this.messages = this.messages.filter(
(msg: Message) =>
!(
(msg.from === f_id && msg.to === id) ||
(msg.from === id && msg.to === f_id)
)
);
if (f_id === this.from) {
console.log("清除对应聊天数据展示栈");
this.corresponding = [];
}
const messages = JSON.parse(JSON.stringify(toRaw(this.historymessages)));
try {
await deleteMessages(id, messages);
} catch (error) {
console.log("删除历史消息时出错" + error);
}
},
},
// 定义计算属性
getters: {
// 获取消息数量
messageCount: (state) => state.messages.length,
},
});
export const messageSignStore = defineStore("messageSignStore", {
state: () => ({
sign: [],
}),
actions: {
addSign(sign) {
// 检查是否已经存在相同的值
const exists = this.sign.some(
(item) =>
item.sender === sign.sender && item.sender_name === sign.sender_name
);
if (!exists) {
this.sign.push(sign);
} else {
console.warn(`Sign "${sign}" already exists and will not be added.`);
}
},
clearSign() {
this.sign = [];
},
removeSign(sign) {
// 找到相同值的索引
const index = this.sign.indexOf(sign);
if (index !== -1) {
// 如果存在,删除该值
this.sign.splice(index, 1);
} else {
console.warn(`Sign "${sign}" not found.`);
}
},
},
});
export const groupMessageStore = defineStore("groupMessageStore", {
state: () => ({
g_id: "",
historymessages: [],
messages: [],
corresponding: [],
}),
actions: {
addMessage(message) {
this.messages.push(message);
},
clearMessages() {
this.messages = [];
},
async initMessages() {
this.historymessages = [...this.historymessages, ...this.messages];
this.messages = [];
},
async getHistoryMessages(u_id, g_id) {
this.g_id = g_id;
const key = `${u_id}-${g_id}`;
try {
this.historymessages = await loadGroupMessages(key);
console.log("缓存到历史消息:");
console.log(this.historymessages);
// 确保历史消息是数组类型
if (!Array.isArray(this.historymessages)) {
console.error("历史消息数据无效:", this.historymessages);
this.historymessages = []; // 如果数据无效,设置为空数组
}
} catch (error) {
console.log("加载历史消息时出错" + error);
}
},
async saveMessagesHistory(u_id, g_id) {
const key = `${u_id}-${g_id}`;
console.log(key);
const messages = toRaw(this.messages);
this.messages = [];
this.historymessages = [];
await saveGroupMessages(key, messages);
},
async deleteMessagesHistory(u_id, g_id) {
const key = `${u_id}-${g_id}`;
this.historymessages = [];
this.messages = [];
await deleteGroupMessages(key, []);
},
},
});

View File

@@ -48,9 +48,9 @@ export const messageStore = defineStore("messageStore", {
this.corresponding = [...historymessages, ...this.corresponding]; this.corresponding = [...historymessages, ...this.corresponding];
}, },
// 清楚当前登录的聊天数据,保存到本地 // 清楚当前登录的聊天数据,保存到本地
async saveMessagesHistory(u_id) { async saveMessagesHistory(id) {
const messages = toRaw(this.messages); const messages = toRaw(this.messages);
await saveMessages(u_id, messages); await saveMessages(id, messages);
}, },
// 加载本地聊天数据 // 加载本地聊天数据
async loadMessagesHistory(u_id) { async loadMessagesHistory(u_id) {

18
src/store/message_sign.ts Normal file
View File

@@ -0,0 +1,18 @@
import { defineStore } from "pinia";
import { ref } from "vue";
export const messageSignStore = defineStore("messageSignStore", ()=>{
const messageSign = ref(false);
const setMessageSign = (sign: boolean)=>{
messageSign.value = sign;
}
const getMessageSign = (): boolean =>{
return messageSign.value;
}
return {
setMessageSign,
getMessageSign
}
});

90
src/store/user.ts Normal file
View File

@@ -0,0 +1,90 @@
import { ref, reactive } from "vue";
import { defineStore } from "pinia";
interface User {
id: number;
u_id: string;
u_name: string;
u_avatar: string;
u_account: string;
u_introduction: string;
}
interface Code {
c_id: string;
code: string;
}
export const userInfoStore = defineStore(
"userInfoStore",
() => {
const token = ref<string | null>(null);
const account = ref<string | null>(null);
const user = reactive<User>({
id: 0,
u_id: "",
u_name: "",
u_avatar: "",
u_account: "",
u_introduction: "",
});
const clearUserInfo = (): void => {
token.value = null;
account.value = null;
user.id = 0;
user.u_id = "";
user.u_name = "";
user.u_avatar = "";
user.u_account = "";
user.u_introduction = "";
};
// const setAccount = (acc: string | null): void => {
// account.value = acc;
// };
// const getAccount = (): string | null => {
// return account.value;
// };
return {
token,
user,
clearUserInfo,
account,
// setAccount,
// getAccount,
};
},
{
persist: {
key: "userInfo",
storage: localStorage,
pick: ["token", "account", "user"],
},
}
);
export const codeStore = defineStore(
"codeStore",
() => {
const codes = reactive<Code>({
c_id: "",
code: "",
});
const setID = (id: string): void => {
codes.c_id = id;
};
const getID = (): string => {
return codes.c_id;
}
return {
setID,
getID
};
},
);

View File

@@ -5,9 +5,9 @@
<el-input v-model="tempsearch" :prefix-icon="Search" style="width: 90%;" clearable></el-input> <el-input v-model="tempsearch" :prefix-icon="Search" style="width: 90%;" clearable></el-input>
<el-scrollbar style="height: 500px;width: 90%;"> <el-scrollbar style="height: 500px;width: 90%;">
<ul class="infinitelist" style="overflow: auto"> <ul class="infinitelist" style="overflow: auto">
<li v-for="(item) in tempsearchResult" :key="item.u_id" class="infinitelistitem" <li v-for="(item) in tempsearchResult" :key="item.id" class="infinitelistitem"
@click="switchTemplate(item.u_id, item.u_name, item.u_avatar)" @click="switchTemplate(item.id, item.u_name, item.u_avatar)"
:class="{ 'selected': selectedFriendId === item.u_id }"> :class="{ 'selected': selectedFriendId === item.id }">
<div class="user-profile"> <div class="user-profile">
<img :src="item.u_avatar" alt="User Avatar" /> <img :src="item.u_avatar" alt="User Avatar" />
<div :class="['status-dot', statusClass]"></div> <div :class="['status-dot', statusClass]"></div>
@@ -23,15 +23,15 @@
<!-- 聊天消息指示框 头像时间内容 --> <!-- 聊天消息指示框 头像时间内容 -->
<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="item.sender !== userinfo.user.u_id ? oppositeAvatar : userinfo.user.u_avatar" <img :src="item.from !== userinfo.user.id ? oppositeAvatar : userinfo.user.u_avatar"
alt="User Avatar" /> 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>
@@ -68,7 +68,8 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="u_name" label="姓名" width="100"></el-table-column> <el-table-column prop="u_name" label="姓名" width="100"></el-table-column>
<el-table-column prop="u_id" label="id" width="100"></el-table-column> <!-- <el-table-column prop="id" label="id" width="0"></el-table-column> -->
<el-table-column prop="u_id" label="u_id" width="100"></el-table-column>
<el-table-column prop="u_introduction" label="个性签名" width="200"></el-table-column> <el-table-column prop="u_introduction" label="个性签名" width="200"></el-table-column>
<el-table-column> <el-table-column>
<template #default="scope"> <template #default="scope">
@@ -77,7 +78,7 @@
</el-table-column> </el-table-column>
<el-table-column> <el-table-column>
<template #default="scope"> <template #default="scope">
<el-button @click="deleteFriend(scope.row.u_id)">删除好友</el-button> <el-button @click="deletefriend(scope.row.id)">删除好友</el-button>
</template> </template>
</el-table-column> </el-table-column>
@@ -88,14 +89,14 @@
<script setup> <script setup>
import { ref, onMounted, nextTick, watch, computed } from 'vue' import { ref, onMounted, nextTick, watch, computed } from 'vue'
import { userInfoStore } from '@/store/store'; import { userInfoStore } from '@/store/user';
import axios from 'axios';
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { onlineSocketStore } from '@/store/Online'; import { onlineSocketStore } from '@/store/Online';
import { messageStore } from '@/store/message'; import { messageStore } from '@/store/message.ts';
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { voiceStore } from '@/store/Voice'; import { voiceStore } from '@/store/Voice';
import { onCallStore } from '@/store/VoiceTarget'; import { onCallStore } from '@/store/VoiceTarget';
import { getFriends, deleteFriend } from '@/api/friend';
const socket = onlineSocketStore() const socket = onlineSocketStore()
const userinfo = userInfoStore() const userinfo = userInfoStore()
@@ -136,40 +137,12 @@ const friendSearchResult = computed(() => {
}) })
//获取好友信息
const getFriends = async () => {
const response = await axios.get('/api/friend/getfriends', { headers: { 'Authorization': userinfo.token } })
if (response.data.code === 200) {
friends.value = response.data.data
} else {
ElMessage.error(response.data.msg)
}
// axios(({
// headers: {
// 'Authorization': userinfo.token
// },
// method: 'GET',
// url: '/api/friend/getfriends'
// })).then((response) => {
// if (response.data.code === 200) {
// friends.value = response.data.data
// }
// else {
// ElMessage.error(response.data.msg)
// }
// }).catch((error) => {
// ElMessage.error('获取好友信息失败,请检查网络连接' + error)
// })
}
//切换聊天对象 //切换聊天对象
const switchTemplate = (u_id, u_name, u_avatar) => { const switchTemplate = (id, u_name, u_avatar) => {
message.sender = u_id message.from = id
oppositeId.value = u_id oppositeId.value = id
oppositeName.value = u_name oppositeName.value = u_name
oppositeAvatar.value = u_avatar oppositeAvatar.value = u_avatar
message.setCorresponding() message.setCorresponding()
@@ -177,7 +150,8 @@ const switchTemplate = (u_id, u_name, u_avatar) => {
inputDisabled.value = false inputDisabled.value = false
placeholder.value = '请输入消息内容' placeholder.value = '请输入消息内容'
scrollToBottom() scrollToBottom()
selectedFriendId.value = u_id selectedFriendId.value = id
console.log(message.corresponding)
} }
// 定义回车键的处理函数 // 定义回车键的处理函数
@@ -189,17 +163,14 @@ const handleEnter = () => {
// 发送一类消息给目标用户 // 发送一类消息给目标用户
const msg = { const msg = {
group: false, cmd: "MESSAGE",
message: true, from: userinfo.user.id,
system: false, to: message.from,
sender: userinfo.user.u_id,
sender_name: userinfo.user.u_name,
target: message.sender,//sender为选中聊天的用户id
content: inputValue.value, content: inputValue.value,
time: new Date().toLocaleString() time: new Date().toLocaleString()
}; };
socket.send(JSON.stringify(msg)) socket.send(msg)
message.addMessage(msg) message.addMessage(msg)
inputValue.value = '' inputValue.value = ''
scrollToBottom() scrollToBottom()
@@ -222,36 +193,26 @@ const scrollToBottom = () => {
// 监听消息 // 监听消息
watch(() => message.messages.length, (newLength, oldLength) => { watch(() => message.messages.length, (newLength, oldLength) => {
const msg = message.messages[newLength - 1] const msg = message.messages[newLength - 1]
if (newLength > oldLength && (msg.sender === message.sender && msg.target === message.target) || (msg.sender === message.target && msg.target === message.sender)) { console.log("监听到新消息:", msg)
if (newLength > oldLength && (msg.from === message.from && msg.to === message.to) || (msg.from === message.to && msg.to === message.from)) {
messagebox.value.push(msg) messagebox.value.push(msg)
scrollToBottom() scrollToBottom()
} }
}) })
const deleteFriend = (u_id) => { const deletefriend = (id) => {
axios({ if(deleteFriend(id)){
headers: {
'Authorization': userinfo.token,
'Content-Type': 'application/json'
},
method: 'POST',
url: '/api/friend/deletefriend',
data: {
u_id: u_id
}
}).then((response) => {
if (response.data.code === 200) {
inputValue.value = '' inputValue.value = ''
inputDisabled.value = true inputDisabled.value = true
placeholder.value = '请选择聊天对象' placeholder.value = '请选择聊天对象'
oppositeAvatar.value = '' oppositeAvatar.value = ''
ElMessage.success('删除好友成功') ElMessage.success('删除好友成功')
getFriends() friends.value = getFriends()
return
}else{
ElMessage.error('删除好友失败')
return
} }
else {
ElMessage.error(response.data.msg)
}
})
} }
const deleteChatHisory = (u_id) => { const deleteChatHisory = (u_id) => {
@@ -278,14 +239,14 @@ const handleConfirmCall = () => {
onMounted(async () => { onMounted(async () => {
await getFriends() friends.value = await getFriends()
await nextTick() await nextTick()
console.log(friends.value) console.log(friends.value)
console.log(message.sender) console.log(message.from)
if (message.sender !== '' || message.sender !== null) { if (message.from !== '' || message.from !== null) {
console.log("切换聊天对象") console.log("切换聊天对象")
const foundUser = friends.value.find((friend) => friend.u_id === message.sender); const foundUser = friends.value.find((friend) => friend.id === message.from);
switchTemplate(message.sender, foundUser.u_name, foundUser.u_avatar) switchTemplate(message.from, foundUser.u_name, foundUser.u_avatar)
} }
}) })

View File

@@ -307,7 +307,7 @@ const sendMessage = () => {
time: new Date().toLocaleString() time: new Date().toLocaleString()
}; };
socket.send(JSON.stringify(msg)) socket.send(msg)
groupMessage.addMessage(msg) groupMessage.addMessage(msg)
message.value = '' message.value = ''
scrollToBottom() scrollToBottom()
@@ -401,7 +401,7 @@ const joinGroup = (g_id) => {
} }
const leaveGroup = async () => { const leaveGroup = async () => {
const response = await axios.post('/api/group/leave', { const response = await axios.get('/api/group/leave', {
params: { params: {
g_id: selectedGroupId.value g_id: selectedGroupId.value
} }

View File

@@ -26,11 +26,11 @@
import { onMounted, watchEffect } from 'vue'; import { onMounted, watchEffect } from 'vue';
import Navbar from '../../components/navBar.vue'; import Navbar from '../../components/navBar.vue';
import UserProfile from '../../components/userProfile.vue'; import UserProfile from '../../components/userProfile.vue';
import { userInfoStore } from '@/store/store'; import { userInfoStore } from '@/store/user';
import { onlineSocketStore } from '@/store/Online'; import { onlineSocketStore } from '@/store/Online';
import { getUserInfo } from '@/functions/user'; import { getUserInfo } from '@/api/user';
import GlobalMessageButton from '@/components/GlobalMessageButton.vue'; import GlobalMessageButton from '@/components/GlobalMessageButton.vue';
import { messageStore } from '@/store/message'; import { messageStore } from '@/store/message.ts';
import phonePanel from '@/components/phonePanel.vue'; import phonePanel from '@/components/phonePanel.vue';
import { voiceStore } from '@/store/Voice'; import { voiceStore } from '@/store/Voice';
import { initDB } from '@/functions/historyMessages'; import { initDB } from '@/functions/historyMessages';
@@ -48,10 +48,10 @@ onMounted(() => {
watchEffect(() => { watchEffect(() => {
if (userinfo.user.u_id) { // u_id if (userinfo.user.u_id) { // u_id
console.log('User ID is available:', userinfo.user.u_id); console.log('User ID is available:', userinfo.user.u_id);
socket.connect(userinfo.user.u_id); // WebSocket socket.connect(userinfo.user.id); // WebSocket
message.target = userinfo.user.u_id; // ID message.to = userinfo.user.id; // ID
voice.connect(userinfo.user.u_id); // // voice.connect(userinfo.user.u_id); //
} }
}); });

View File

@@ -6,15 +6,18 @@
<span style="font-size: 30px;">收到的邀请</span> <span style="font-size: 30px;">收到的邀请</span>
<el-scrollbar style="height: 300px;width: 900px;"> <el-scrollbar style="height: 300px;width: 900px;">
<div class="inviteItem"> <div class="inviteItem">
<div v-for="item in friendInviting" :key="item.inviter"> <div v-for="item in friendInviting" :key="item.i_id">
<div v-if="item.status === 0">
<div class="inviteAvatar"> <div class="inviteAvatar">
<img :src="item.inviter_avatar" alt="莫得"> <img :src="item.inviter_avatar" alt="莫得">
</div> </div>
<div style="width: 700px;display: inline-block;"> <div style="width: 700px;display: inline-block;">
<span style="font-size: 20px;">用户{{ item.inviter_name }} 邀请您成为好友</span> <span style="font-size: 20px;">用户 {{ item.inviter }} {{ item.inviter_name }} {{ item.i_id }} 邀请您成为好友</span>
</div> </div>
<el-button style="width: 50px;" @click="acceptFriend(item.inviter)">接受</el-button> <el-button style="width: 50px;" @click="handleinviting('accept',item)">接受</el-button>
<el-button style="width: 50px;" @click="rejectFriend(item.inviter)">拒绝</el-button> <el-button style="width: 50px;" @click="handleinviting('reject',item)">拒绝</el-button>
</div>
</div> </div>
</div> </div>
<div class="inviteItem"> <div class="inviteItem">
@@ -25,8 +28,8 @@
<div style="width: 700px;display: inline-block;"> <div style="width: 700px;display: inline-block;">
<span style="font-size: 20px;">{{ item.inviter_name }} 邀请您加入房间{{ item.room }}</span> <span style="font-size: 20px;">{{ item.inviter_name }} 邀请您加入房间{{ item.room }}</span>
</div> </div>
<el-button style="width: 50px;" @click="acceptRoom(item.inviter, item.room)">接受</el-button> <el-button style="width: 50px;" @click="handleinviting('accept',item)">接受</el-button>
<el-button style="width: 50px;" @click="rejectRoom(item.inviter, item.room)">拒绝</el-button> <el-button style="width: 50px;" @click="handleinviting('reject',item)">拒绝</el-button>
</div> </div>
</div> </div>
</el-scrollbar> </el-scrollbar>
@@ -59,18 +62,16 @@
</template> </template>
<script setup> <script setup>
import axios from 'axios';
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { userInfoStore, messagePointStore } from '@/store/store'; import { userInfoStore, messagePointStore } from '@/store/store';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { messageStore, messageSignStore } from '@/store/message'; import { messageStore, messageSignStore } from '@/store/message';
import router from '@/router'; import router from '@/router';
import { getInvitings,handleInviting } from '@/api/inviting';
const message = messageStore() const message = messageStore()
const messageSign = messageSignStore() const messageSign = messageSignStore()
const userinfo = userInfoStore()
const messagePoint = messagePointStore() const messagePoint = messagePointStore()
const allInviting = ref([]) const allInviting = ref([])
@@ -83,142 +84,63 @@ const allRead = () => {
messageSign.clearSign() messageSign.clearSign()
} }
const getAllInviting = () => { const getAllInviting = async () => {
axios({ try{
headers: { const data = await getInvitings()
'Authorization': userinfo.token if(data.code === "200"){
}, allInviting.value = data.data.result
method: 'GET', }else{
url: '/api/inviting/getinvitings' ElMessage.error("获取邀请失败,请稍后再试:"+data.message)
}).then(res => { }
allInviting.value = res.data.data }catch(error){
}).catch((error) => { ElMessage.error("获取邀请失败:",error)
console.log(error) }
})
} }
const acceptFriend = (inviter) => { const handleinviting = async (status,item) =>{
axios({ let index = -1;
headers: { switch(status){
'Authorization': userinfo.token, case "accept":
'Content-Type': 'application/json', await handleInviting({
}, i_id: item.i_id,
method: 'POST', inviter: item.inviter,
url: '/api/inviting/passinviting', target: item.target,
data: { room: item.room,
inviter: inviter, status: 1,
target: userinfo.user.u_id time: item.time
} })
}).then(response => { index = allInviting.value.findIndex(i => i.i_id === item.i_id);
if (response.data.code === 200) {
ElMessage.success('接受成功');
// 从 allInviting 中删除当前邀请项
const index = allInviting.value.findIndex(item => item.inviter === inviter && item.room === null);
console.log(index)
if (index !== -1) { if (index !== -1) {
allInviting.value.splice(index, 1); // 修改 allInvitingcomputed 属性会自动更新 allInviting.value.splice(index, 1);
} }
} ElMessage.success("操作成功")
break;
else case "reject":
ElMessage.error('系统错误,请稍后再试') await handleInviting({
}).catch((error) => { i_id: item.i_id,
console.log(error) inviter: item.inviter,
target: item.target,
room: item.room,
status: 2,
time: item.time
}) })
} index = allInviting.value.findIndex(i => i.i_id === item.i_id);
const rejectFriend = (inviter) => {
axios({
headers: {
'Authorization': userinfo.token,
'Content-Type': 'application/json',
},
method: 'POST',
url: '/api/inviting/refuseinviting',
data: {
inviter: inviter,
target: userinfo.user.u_id
}
}).then(response => {
if (response.data.code === 200) {
ElMessage.success('拒绝成功');
// 从 allInviting 中删除当前邀请项
const index = allInviting.value.findIndex(item => item.inviter === inviter && item.room === null);
console.log(index)
if (index !== -1) { if (index !== -1) {
allInviting.value.splice(index, 1); // 修改 allInvitingcomputed 属性会自动更新 allInviting.value.splice(index, 1);
} }
ElMessage.success("操作成功")
break;
default:
ElMessage.error("操作失败,未知操作类型")
break;
} }
else
ElMessage.error('系统错误,请稍后再试')
}).catch((error) => {
console.log(error)
})
} }
const acceptRoom = (inviter, room) => {
axios({
headers: {
'Authorization': userinfo.token,
'Content-Type': 'application/json',
},
method: 'POST',
url: '/api/inviting/passinviting',
data: {
inviter: inviter,
target: userinfo.user.u_id,
room: room
}
}).then(response => {
if (response.data.code === 200) {
ElMessage.success('拒绝成功')
// 从 allInviting 中删除当前邀请项
const index = allInviting.value.findIndex(item => item.inviter === inviter && item.room === room);
console.log(index)
if (index !== -1) {
allInviting.value.splice(index, 1); // 修改 allInvitingcomputed 属性会自动更新
}
}
else
ElMessage.error('系统错误,请稍后再试')
}).catch((error) => {
console.log(error)
})
}
const rejectRoom = (inviter, room) => {
axios({
headers: {
'Authorization': userinfo.token,
'Content-Type': 'application/json',
},
method: 'POST',
url: '/api/inviting/refuseinviting',
data: {
inviter: inviter,
target: userinfo.user.u_id,
room: room
}
}).then(response => {
if (response.data.code === 200) {
ElMessage.success('拒绝成功')
// 从 allInviting 中删除当前邀请项
const index = allInviting.value.findIndex(item => item.inviter === inviter && item.room === room);
console.log(index)
if (index !== -1) {
allInviting.value.splice(index, 1); // 修改 allInvitingcomputed 属性会自动更新
}
}
else
ElMessage.error('系统错误,请稍后再试')
}).catch((error) => {
console.log(error)
})
}
const directionToFriend = (item) => { const directionToFriend = (item) => {
// 跳转到好友聊天页面 // 跳转到好友聊天页面
message.sender = item.sender message.from = item.from
messageSign.removeSign(item) messageSign.removeSign(item)
router.push('/home/friends') router.push('/home/friends')
} }
@@ -233,7 +155,6 @@ const directionToGroup = (id) => {
onMounted(() => { onMounted(() => {
getAllInviting() getAllInviting()
console.log(messageSign.sign)
}) })

View File

@@ -6,11 +6,11 @@
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-input v-model="inputValue" style="width: 80%" size="large" placeholder="Please Input" clearable <el-input v-model="inputValue" style="width: 80%" size="large" placeholder="Please Input" clearable
:prefix-icon="Search" @keyup.enter="searchFriend" /> :prefix-icon="Search" @keyup.enter="searchfriend" />
<el-button class="search" size="large" @click="searchFriend">搜索</el-button> <el-button class="search" size="large" @click="searchfriend">搜索</el-button>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-table :data="friendSearchResult" style="width: 100%;height: 200px;"> <el-table :data="friendSearchResultFiltered" style="width: 100%;height: 200px;">
<el-table-column prop="u_avatar" label="" width="100"> <el-table-column prop="u_avatar" label="" width="100">
<template #default="scope"> <template #default="scope">
<!-- 使用 el-avatar 组件显示头像 --> <!-- 使用 el-avatar 组件显示头像 -->
@@ -18,11 +18,11 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="u_name" label="姓名" width="125"></el-table-column> <el-table-column prop="u_name" label="姓名" width="125"></el-table-column>
<el-table-column prop="u_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 prop="u_introduction" label="个性签名" width="200"></el-table-column> <el-table-column prop="u_introduction" label="个性签名" width="200"></el-table-column>
<el-table-column> <el-table-column>
<template #default="scope"> <template #default="scope">
<el-button @click="addFriend(scope.row.u_id)">添加好友</el-button> <el-button @click="addfriend(scope.row.id)">添加好友</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -34,8 +34,8 @@
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-input v-model="inputValue1" style="width: 80%" size="large" placeholder="Please Input" clearable <el-input v-model="inputValue1" style="width: 80%" size="large" placeholder="Please Input" clearable
:prefix-icon="Search" @keyup.enter="searchPlayRoom" /> :prefix-icon="Search" @keyup.enter="searchplayroom" />
<el-button class="search" size="large" @click="searchPlayRoom">搜索</el-button> <el-button class="search" size="large" @click="searchplayroom">搜索</el-button>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-table :data="roomSearchResult" style="width: 100%;height: 200px;"> <el-table :data="roomSearchResult" style="width: 100%;height: 200px;">
@@ -62,12 +62,14 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref, computed } from 'vue'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import axios from 'axios' import axios from 'axios'
import { userInfoStore } from '@/store/store' import { userInfoStore } from '@/store/user'
import { onlineSocketStore } from '@/store/Online' import { onlineSocketStore } from '@/store/Online'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { searchFriend,addFriend } from '@/api/friend'
import { searchPlayRoom } from '@/api/room'
const socket = onlineSocketStore() const socket = onlineSocketStore()
const inputValue = ref('') const inputValue = ref('')
@@ -76,78 +78,48 @@ const userinfo = userInfoStore()
const friendSearchResult = ref([]) const friendSearchResult = ref([])
const excludeId = ref(userinfo.user?.id ?? '')
const friendSearchResultFiltered = computed(() => {
if (!excludeId.value) return friendSearchResult.value
return friendSearchResult.value.filter(item => item.id !== excludeId.value)
})
const roomSearchResult = ref([]) const roomSearchResult = ref([])
// 定义回车键的处理函数 // 定义回车键的处理函数
const searchFriend = () => { const searchfriend = async () => {
console.log(inputValue.value); try{
axios({ friendSearchResult.value = await searchFriend(inputValue.value)
headers: { ElMessage.success("搜索成功")
'Authorization': userinfo.token }catch(error){
}, console.error("搜索好友失败:",error)
method: 'GET',
url: '/api/friend/searchuser',
params: {
u_name: inputValue.value
} }
}).then((response) => {
if (response.data.code === 200) {
friendSearchResult.value = response.data.data
console.log(friendSearchResult.value)
}
})
}; };
//搜索playRoom //搜索playRoom
const searchPlayRoom = () => { const searchplayroom = async () => {
axios({ try{
headers: { roomSearchResult.value = await searchPlayRoom(inputValue1.value)
'Content-Type': 'application/json', }catch(error){
'Authorization': userinfo.token console.error("搜索房间失败:",error)
},
method: 'GET',
url: '/api/room/search',
params: {
r_name: inputValue1.value
} }
}).then((response) => {
if (response.data.code === 200) {
roomSearchResult.value = response.data.data
console.log(roomSearchResult.value)
}
})
inputValue1.value = ''
} }
//添加好友逻辑 //添加好友逻辑
const addFriend = (u_id) => { const addfriend = async (f_id) => {
axios({ try{
headers: { const data = await addFriend(f_id)
'Authorization': userinfo.token if(data.code === "200"){
}, ElMessage.success("请求发送成功,等待对方验证")
method: 'POST', }else{
url: '/api/inviting/sendinviting', ElMessage.error(data.message)
data: {
inviter: userinfo.user.u_id,
target: u_id
} }
}).then((response) => { }catch(error){
if (response.data.code === 200) { console.error("添加好友失败:",error)
ElMessage.success('已发送申请,等待对方处理')
const message = {
message: false,
system: false,
target: u_id,
sender_name: userinfo.user.u_name
} }
socket.send(JSON.stringify(message))
}
else {
ElMessage.error('发送失败,请稍后再试 msg:' + response.data.msg)
}
})
} }
//加入房间申请逻辑 //加入房间申请逻辑

View File

@@ -71,7 +71,7 @@ const uploadAvatar = () => {
axios({ axios({
headers: { headers: {
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data',
'Authorization': userinfo.token 'Authorization': 'Bearer '+ userinfo.token
}, },
method: 'post', method: 'post',
url: '/api/avatar/upload', url: '/api/avatar/upload',
@@ -104,7 +104,7 @@ const submitForm = async () => {
{ {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': userinfo.token 'Authorization': 'Bearer ' + userinfo.token
} }
}); });
if (response.data.code === 200) { if (response.data.code === 200) {

View File

@@ -2,7 +2,7 @@
<div class="container"> <div class="container">
<div class="login-container"> <div class="login-container">
<h2>登录</h2> <h2>登录</h2>
<form @submit.prevent="handleLogin"> <form @submit.prevent="login(account, password)">
<div class="form-group"> <div class="form-group">
<label for="account">邮箱</label> <label for="account">邮箱</label>
<input type="text" id="account" v-model="account" required /> <input type="text" id="account" v-model="account" required />
@@ -24,41 +24,12 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import axios from 'axios'; import { login } from '@/api/login.ts';
import { useRouter } from 'vue-router';
import { userInfoStore } from '@/store/store.js';
import SHA256 from 'crypto-js/sha256';
const userinfo = userInfoStore();
const account = ref(''); const account = ref('');
const password = ref(''); const password = ref('');
const encryptedPassword = ref('');
const router = useRouter();
const handleLogin = async () => {
try {
encryptedPassword.value = SHA256(password.value).toString();
const response = await axios.post('/api/login', {
u_account: account.value,
u_password: encryptedPassword.value,
}, {
headers: {
'Content-Type': 'application/json',
}
});
if (response.data.code === 200) {
userinfo.token = response.data.data;
userinfo.user.u_account = account.value;
console.log(userinfo.user.u_account);
router.replace({ name: 'home' });
} else {
alert('账号或密码错误!' + response.data.msg);
}
} catch (error) {
console.error('Login error:', error);
alert('Login failed. Please try again later.');
}
};
</script> </script>
<style scoped> <style scoped>

View File

@@ -4,11 +4,11 @@
<form @submit.prevent="handleRegister"> <form @submit.prevent="handleRegister">
<div class="form-group"> <div class="form-group">
<label for="u_account">邮箱</label> <label for="u_account">邮箱</label>
<input type="u_account" id="u_account" v-model="u_account" required /> <input type="u_account" id="u_account" v-model="account" required />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="u_password">密码</label> <label for="u_password">密码</label>
<input type="u_password" id="u_password" v-model="u_password" required /> <input type="u_password" id="u_password" v-model="password" required />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="confirm-u_password">确认密码</label> <label for="confirm-u_password">确认密码</label>
@@ -35,9 +35,12 @@ import { useRouter } from 'vue-router';
import { ElMessageBox, ElMessage } from 'element-plus'; import { ElMessageBox, ElMessage } from 'element-plus';
import { userInfoStore } from '@/store/store.js'; import { userInfoStore } from '@/store/store.js';
import SHA256 from 'crypto-js/sha256'; import SHA256 from 'crypto-js/sha256';
import { register } from '@/api/login';
import { codeStore } from '@/store/user';
import { sendCode } from '@/api/code';
const u_account = ref(''); const account = ref('');
const u_password = ref(''); const password = ref('');
const encryptedPassword = ref(''); const encryptedPassword = ref('');
const confirmPassword = ref(''); const confirmPassword = ref('');
const verificationCode = ref(''); const verificationCode = ref('');
@@ -49,45 +52,33 @@ const userinfo = userInfoStore();
const router = useRouter(); const router = useRouter();
const codeinfo = codeStore();
// 定义邮箱验证的正则表达式 // 定义邮箱验证的正则表达式
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
// 使用 computed 属性来验证邮箱格式 // 使用 computed 属性来验证邮箱格式
const isEmailValid = computed(() => { const isEmailValid = computed(() => {
return emailRegex.test(u_account.value); return emailRegex.test(account.value);
}); });
// 发送验证码 // 发送验证码
const sendVerificationCode = () => { const sendVerificationCode = () => {
if (isCountingDown.value) return; if (isCountingDown.value) return;
console.log(isEmailValid.value) console.log(isEmailValid.value)
if (!isEmailValid.value) { if (!isEmailValid.value) {
ElMessage.error("请输入正确的邮箱格式"); ElMessage.error("请输入正确的邮箱格式");
return; return;
} }
console.log(u_account.value) if(sendCode(account.value)){
axios({ ElMessage.success("验证码发送成功,请注意查收");
headers: {
'Content-Type': 'application/json'
},
method: "post",
url: "/api/code/sendcode",
data: JSON.stringify({ u_account: u_account.value })
})
.then((response) => {
if (response.data && response.data.code !== 200) {
ElMessageBox.alert("注册失败,请重新填写 msg:" + (response.data.msg || '未知错误'), '注册失败');
} else {
startCountdown(); startCountdown();
codeId.value = response.data.data; }else{
console.log(codeId); ElMessage.error("验证码发送失败,请稍后重试");
ElMessageBox.alert('发送成功,请耐心等待', '请求成功'); // console.error("验证码发送失败,请稍后重试");
} }
})
.catch((error) => {
console.error("请求失败:", error);
ElMessageBox.alert("请求失败,请稍后再试", '网络错误');
});
}; };
@@ -106,71 +97,14 @@ const startCountdown = () => {
// 注册逻辑 // 注册逻辑
const handleRegister = () => { const handleRegister = () => {
//验证验证码是否正确 if (password.value !== confirmPassword.value) {
axios({
headers: {
'Content-Type': 'application/json'
},
method: "post",
url: "/api/code/verifycode",
data: JSON.stringify({
v_id: codeId.value,
code: verificationCode.value
})
}).then((response) => {
console.log(verificationCode)
if (response.data.code === 210)
ElMessageBox.alert("验证码过期")
else if (response.data.code === 211)
ElMessageBox.alert("验证码错误")
else {
//验证成功,提交注册表单
if (u_password.value !== confirmPassword.value) {
ElMessageBox.alert("两次密码不一致", '注册失败') ElMessageBox.alert("两次密码不一致", '注册失败')
return; return;
} }
encryptedPassword.value = SHA256(u_password.value).toString(); register({
axios({ code: verificationCode.value,
headers: { account: account.value,
'Content-Type': 'application/json' password: password.value
},
method: 'post',
url: '/api/register',
data: JSON.stringify({
u_account: u_account.value,
u_password: encryptedPassword.value
})
}).then((response) => {
if (response.data.code === 212) ElMessageBox.alert("该账号已注册", '注册失败')
if (response.data.code === 200) {
axios({
headers: {
'Content-Type': 'application/json'
},
method: 'post',
url: '/api/login',
data: JSON.stringify({
u_account: u_account.value,
u_password: encryptedPassword.value
})
}).then((response) => {
userinfo.token = response.data.data;
userinfo.user.u_account = u_account.value;
ElMessage.success("注册成功,正在跳转");
}).catch((error) => {
console.error("登录失败:", error);
ElMessageBox.alert("登录失败,请稍后再试", '网络错误');
});
router.push({ name: 'init' }).catch(err => {
console.error("Routing error:", err)
});
}
}).catch((error) => {
ElMessageBox.alert("错误")
})
}
}) })
}; };
</script> </script>

View File

@@ -26,8 +26,8 @@
<el-button @click="dialogVisibleChangeName = true">修改名字</el-button> <el-button @click="dialogVisibleChangeName = true">修改名字</el-button>
</el-row> </el-row>
<el-row> <el-row>
<span style="width: 80%;">账号{{ userinfo.user.u_account }}</span> <span style="width: 80%;">账号{{ userinfo.account }}</span>
<el-button @click="dialogVisibleChangeAccount = true">修改账号</el-button> <!-- <el-button @click="dialogVisibleChangeAccount = true">修改账号</el-button> -->
</el-row> </el-row>
<el-row> <el-row>
<span style="width: 80%;">个性签名{{ userinfo.user.u_introduction }}</span> <span style="width: 80%;">个性签名{{ userinfo.user.u_introduction }}</span>
@@ -132,14 +132,14 @@
</template> </template>
<script setup> <script setup>
import { userInfoStore } from '@/store/store'; import { userInfoStore } from '@/store/user';
import axios from 'axios'; import axios from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { getUserInfo } from '@/functions/user'; import { getUserInfo } from '@/functions/user';
const userinfo = userInfoStore(); const userinfo = userInfoStore();
console.log(userinfo.user.u_account); console.log(userinfo.account);
const dialogVisibleChangeName = ref(false) const dialogVisibleChangeName = ref(false)

View File

@@ -1,25 +1,21 @@
import { ref } from "vue"; import { ref } from "vue";
import { userInfoStore } from "@/store/store"; import { userInfoStore } from "@/store/store";
import { messagePointStore } from "@/store/store"; import { messageStore } from "@/store/message";
import {
messageStore,
messageSignStore,
groupMessageStore,
} from "@/store/message";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { messageSignStore } from "@/store/message_sign";
// 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();
// WebSocket 实例 // WebSocket 实例
const socket = ref(null); const socket = ref(null);
//messagePoint 实例 const messageSign = messageSignStore();
const messagePoint = messagePointStore();
const isManualClose = ref(false); const isManualClose = ref(false);
@@ -58,7 +54,7 @@ export const getIsManualClose = () => {
export const connectWebSocket = () => { export const connectWebSocket = () => {
const protocol = window.location.protocol === "https:" ? "wss://" : "ws://"; const protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
const host = window.location.host; const host = window.location.host;
const socketUrl = `${protocol}${host}/online?u_id=${userinfo.user.u_id}&u_name=${userinfo.user.u_name}`; const socketUrl = `${protocol}${host}/ws/online?name=${userinfo.user.u_name}`;
// const socketUrl = `ws://localhost:8080/online?u_id=${userinfo.user.u_id}&u_name=${userinfo.user.u_name}`; // const socketUrl = `ws://localhost:8080/online?u_id=${userinfo.user.u_id}&u_name=${userinfo.user.u_name}`;
if (socket.value && socket.value.readyState !== WebSocket.CLOSED) { if (socket.value && socket.value.readyState !== WebSocket.CLOSED) {
@@ -71,7 +67,7 @@ export const connectWebSocket = () => {
return; return;
} }
console.log(retrytime); console.log(retrytime);
socket.value = new WebSocket(socketUrl); socket.value = new WebSocket(socketUrl, "token-"+ userinfo.token);
socket.value.onopen = (event) => { socket.value.onopen = (event) => {
console.log("WebSocket连接已建立", event); console.log("WebSocket连接已建立", event);
@@ -85,61 +81,80 @@ export const connectWebSocket = () => {
console.log("从服务器收到消息:", event.data); console.log("从服务器收到消息:", event.data);
try{ try{
const MessageData = JSON.parse(event.data); const MessageData = JSON.parse(event.data);
//是否为上下线消息(三类消息) const cmd = MessageData.cmd;
if (!MessageData.system) { switch(cmd){
//是否为新消息 case "MESSAGE":
if (MessageData.message) {
messagePoint.hasNewMessage = true;
if (MessageData.group) {
//四类消息,群聊消息
console.log("有新群消息");
groupMessage.addMessage(MessageData);
console.log(groupMessage.messages);
messageSign.addSign({
sender_name: MessageData.sender_name,
g_id: MessageData.g_id,
g_name: MessageData.g_name,
});
} else {
//一类消息,私聊消息
console.log("有新消息");
message.addMessage(MessageData); message.addMessage(MessageData);
console.log(message.messages); // messageSign.setMessageSign(true);
messageSign.addSign({ // ElMessage.info("您有一条新的消息");
sender: MessageData.sender, break;
sender_name: MessageData.sender_name, case "PERSONAL_NOTIFY":
}); messageSign.setMessageSign(true);
} ElMessage.info("您有一条新的邀请消息");
} break;
} else {
if (MessageData.engaged) {
// 五类消息,账号重复登录逻辑
setIsManualClose(true);
disconnectWebSocket();
ElMessage("账号重复登录,请注意密码泄露");
console.log("账号重复登陆");
window.location.replace("/");
} else {
//三类消息,指示用户上下线
if (MessageData.status === "online")
ElMessage("用户:" + MessageData.u_name + "上线");
else {
ElMessage("用户:" + MessageData.u_name + "下线");
}
}
} }
}catch(error){ }catch(error){
console.error("解析 JSON 失败:", error); console.error("解析 JSON 失败:", error);
} }
// try {
// const MessageData = JSON.parse(event.data);
// //是否为上下线消息(三类消息)
// if (!MessageData.system) {
// //是否为新消息
// if (MessageData.message) {
// messagePoint.hasNewMessage = true;
// if (MessageData.group) {
// //四类消息,群聊消息
// console.log("有新群消息");
// groupMessage.addMessage(MessageData);
// console.log(groupMessage.messages);
// messageSign.addSign({
// sender_name: MessageData.sender_name,
// g_id: MessageData.g_id,
// g_name: MessageData.g_name,
// });
// } else {
// //一类消息,私聊消息
// console.log("有新消息");
// message.addMessage(MessageData);
// console.log(message.messages);
// messageSign.addSign({
// sender: MessageData.sender,
// sender_name: MessageData.sender_name,
// });
// }
// }
// } else {
// if (MessageData.engaged) {
// // 五类消息,账号重复登录逻辑
// setIsManualClose(true);
// disconnectWebSocket();
// ElMessage("账号重复登录,请注意密码泄露");
// console.log("账号重复登陆");
// window.location.replace("/");
// } else {
// //三类消息,指示用户上下线
// if (MessageData.status === "online")
// ElMessage("用户:" + MessageData.u_name + "上线");
// else {
// ElMessage("用户:" + MessageData.u_name + "下线");
// }
// }
// }
// } catch (error) {
// console.error("解析 JSON 失败:", error);
// }
}; };
socket.value.onerror = (error) => { socket.value.onerror = (error) => {
console.error("WebSocket发生错误", error); console.error("WebSocket发生错误", error);
// console.log(error);
setReconnectScheduled(true); setReconnectScheduled(true);
socket.value.close(); socket.value.close();
}; };
socket.value.onclose = (event) => { socket.value.onclose = (event) => {
message.saveMessagesHistory(userinfo.user.id);
if (!getIsManualClose()) { if (!getIsManualClose()) {
if (getReconnectScheduled()) { if (getReconnectScheduled()) {
socket.value = null; socket.value = null;
@@ -147,7 +162,7 @@ export const connectWebSocket = () => {
setTimeout(reConnectWebSocket, 5000); setTimeout(reConnectWebSocket, 5000);
setReconnectScheduled(false); setReconnectScheduled(false);
} else { } else {
console.log("websocket因为浏览器省电设置断开"); // console.log("websocket因为浏览器省电设置断开");
console.log("WebSocket连接已关闭", event); console.log("WebSocket连接已关闭", event);
} }
} }

View File

View File

@@ -21,16 +21,16 @@ export default defineConfig({
// rewrite: (path) => path.replace(/^\/proxy/, ''), // 重写路径,去掉 /api 前缀 // rewrite: (path) => path.replace(/^\/proxy/, ''), // 重写路径,去掉 /api 前缀
}, },
'/online': { '/ws': {
target: 'ws://localhost:8080', target: 'ws://localhost:8080',
changeOrigin: true, changeOrigin: true,
ws: true, ws: true,
}, },
'/voice': { // '/ws': {
target: 'ws://localhost:8080', // target: 'ws://localhost:8080',
changeOrigin: true, // changeOrigin: true,
ws: true, // ws: true,
} // }
}, },
}, },
// server: { // server: {