添加avatar修改、聊天显示头像,聊天有消息提前

This commit is contained in:
junleea 2025-01-01 18:44:48 +08:00
parent 3f16b7a0b8
commit e41b5ef5ff
2 changed files with 257 additions and 123 deletions

View File

@ -1,14 +1,16 @@
<template> <template>
<el-dialog <el-dialog
title="图片发送" title="图片发送"
v-model="sendImgDialogVisible" v-model="sendImgDialogVisible"
width="60%" width="60%"
height="60%" height="60%"
center center
> >
<!-- 图片输入 --> <!-- 图片输入 -->
<input type="file" @change="handleFileUpload" /> <input type="file" @change="handleFileUpload" />
<el-button type="primary" size="default" @click="sendImageOrVideo" >发送</el-button> <el-button type="primary" size="default" @click="sendImageOrVideo"
>发送</el-button
>
</el-dialog> </el-dialog>
<el-container <el-container
@ -25,9 +27,7 @@
<el-menu :default-openeds="['1', '3']"> <el-menu :default-openeds="['1', '3']">
<el-sub-menu index="1"> <el-sub-menu index="1">
<template #title> <template #title>
<el-icon> <el-icon> <message /> </el-icon>
<message /> </el-icon
>联系人
</template> </template>
<el-scrollbar height="300px"> <el-scrollbar height="300px">
<el-menu-item-group v-for="user in filteredUsers" :key="user.id"> <el-menu-item-group v-for="user in filteredUsers" :key="user.id">
@ -36,7 +36,9 @@
@click="handleGetMessage(user.id)" @click="handleGetMessage(user.id)"
style="height: 40px" style="height: 40px"
> >
<el-avatar style="float: left" :src="user.avatar"></el-avatar>
{{ user.name }} {{ user.name }}
<span v-if="hasUnreadMsg[user.id]" class="unread-dot"></span> <span v-if="hasUnreadMsg[user.id]" class="unread-dot"></span>
</el-menu-item> </el-menu-item>
</el-menu-item-group> </el-menu-item-group>
@ -47,7 +49,7 @@
<el-icon><icon-menu /></el-icon> <el-icon><icon-menu /></el-icon>
群组 群组
</template> </template>
<el-scrollbar height="200px"> <el-scrollbar height="250px">
<el-menu-item-group v-for="item in groupList" :key="item.ID"> <el-menu-item-group v-for="item in groupList" :key="item.ID">
<!-- <template #title>Group 1</template> --> <!-- <template #title>Group 1</template> -->
<el-menu-item <el-menu-item
@ -116,9 +118,10 @@
style="margin-left: 10px; margin-bottom: 8px" style="margin-left: 10px; margin-bottom: 8px"
> >
<el-row class="row-bg" type="flex" align="middle"> <el-row class="row-bg" type="flex" align="middle">
<el-avatar size="default" fit="fit">{{ <!-- <el-avatar size="default" fit="fit">{{
cur_user_name cur_user_name
}}</el-avatar> }}</el-avatar> -->
<el-avatar size="default" :src="cur_user_avatar"></el-avatar>
<span style="margin-left: 10px" <span style="margin-left: 10px"
>{{ cur_user_name }} : {{ formatTime(item.CreatedAt) }}</span >{{ cur_user_name }} : {{ formatTime(item.CreatedAt) }}</span
> >
@ -143,9 +146,17 @@
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="1000" :offset="1" class="msg" style="max-width: 50%;"> <el-col
:span="1000"
:offset="1"
class="msg"
style="max-width: 50%"
>
<!-- {{ item.Msg }} --> <!-- {{ item.Msg }} -->
<div v-html="renderMarkdown(item.Msg)" style="max-width: 50%;"></div> <div
v-html="renderMarkdown(item.Msg)"
style="max-width: 50%"
></div>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -159,56 +170,64 @@
{{ tokenData.username }} : {{ tokenData.username }} :
{{ formatTime(item.CreatedAt) }}</span {{ formatTime(item.CreatedAt) }}</span
> >
<el-avatar size="default" fit="fit"> <el-avatar size="default" :src="tokenData.avatar"></el-avatar>
{{ tokenData.username }}
</el-avatar>
</el-row> </el-row>
<el-row justify="end"> <el-row justify="end">
<el-col :span="1000" class="msg2" style="margin-right: 20px; max-width: 50%;"> <el-col
:span="1000"
class="msg2"
style="margin-right: 20px; max-width: 50%"
>
<!-- {{ item.Msg }} --> <!-- {{ item.Msg }} -->
<div v-html="renderMarkdown(item.Msg)" style="max-width: 50%;"></div> <div
v-html="renderMarkdown(item.Msg)"
style="max-width: 50%"
></div>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-main> </el-main>
<div></div>
<div> <div>
</div> <el-row
<div> class="row-bg"
type="flex"
<el-row class="row-bg" type="flex" justify="space-around" align="middle"> justify="space-around"
<el-button align="middle"
type="primary" >
size="default"
@click="SendImage"
class="send-image-button-bg"
>
文件发送
</el-button>
<el-col :span="20">
<el-input
type="textarea"
style="width: 100%"
rows="1"
autofocus
@keyup.enter="handleSendBtnClick"
placeholder="请输入消息按Enter发送"
v-model="currentMsg"
></el-input>
</el-col>
<el-col :span="2">
<el-button <el-button
class="sendBtn"
@click="handleSendBtnClick"
type="primary" type="primary"
size="default" size="default"
@click="SendImage"
class="send-image-button-bg"
> >
发送 文件发送
</el-button> </el-button>
</el-col> <el-col :span="20">
</el-row> <el-input
type="textarea"
style="width: 100%"
rows="1"
autofocus
@keyup.enter="handleSendBtnClick"
placeholder="请输入消息按Enter发送"
v-model="currentMsg"
></el-input>
</el-col>
<el-col :span="2">
<el-button
class="sendBtn"
@click="handleSendBtnClick"
type="primary"
size="default"
>
发送
</el-button>
</el-col>
</el-row>
</div> </div>
</el-container> </el-container>
</el-container> </el-container>
@ -217,12 +236,12 @@
<script> <script>
import axios from "axios"; import axios from "axios";
import { inject } from "vue"; import { inject } from "vue";
import { ref } from 'vue'; import { ref } from "vue";
import { getFriendListService } from "@/api/chat.js"; import { getFriendListService } from "@/api/chat.js";
import { getMessageService } from "@/api/chat.js"; import { getMessageService } from "@/api/chat.js";
import { sendMessageService } from "@/api/chat.js"; import { sendMessageService } from "@/api/chat.js";
import { UploadFileService } from "@/api/tool.js"; import { UploadFileService } from "@/api/tool.js";
import {GetFileInfoByMd5Service } from "@/api/tool.js"; import { GetFileInfoByMd5Service } from "@/api/tool.js";
import { import {
ElAvatar, ElAvatar,
@ -241,8 +260,8 @@ import {
import { Menu as IconMenu, Message, Setting } from "@element-plus/icons-vue"; import { Menu as IconMenu, Message, Setting } from "@element-plus/icons-vue";
import router from "@/router/index.js"; import router from "@/router/index.js";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import MarkdownIt from 'markdown-it'; import MarkdownIt from "markdown-it";
import CryptoJS from 'crypto-js'; import CryptoJS from "crypto-js";
export default { export default {
data() { data() {
@ -250,13 +269,17 @@ export default {
ip: "", ip: "",
tableData: [], tableData: [],
sendImgDialogVisible: false, sendImgDialogVisible: false,
file : null, file: null,
file_md5: "", file_md5: "",
tokenData: { tokenData: {
token: localStorage.getItem("token"), token: localStorage.getItem("token"),
ip: localStorage.getItem("ip"), ip: localStorage.getItem("ip"),
userId: localStorage.getItem("userId"), userId: localStorage.getItem("userId"),
username: localStorage.getItem("username"), username: localStorage.getItem("username"),
avatar:
localStorage.getItem("avatar") === ""
? "https://gep.ljsea.top/tool/file/9f29cc99-1054-4aff-ab37-e7c0016dd1b5.jpeg"
: localStorage.getItem("avatar"),
}, },
username: localStorage.getItem("username"), username: localStorage.getItem("username"),
userList: [], userList: [],
@ -270,6 +293,7 @@ export default {
md: new MarkdownIt(), //md md: new MarkdownIt(), //md
cur_user_name: "", cur_user_name: "",
cur_user_avatar: "",
eventSource: null, // eventSource: null, //
uid: localStorage.getItem("userId"), uid: localStorage.getItem("userId"),
currentMsg: "", currentMsg: "",
@ -292,8 +316,13 @@ export default {
let data = result.data; let data = result.data;
this.userList = data.friends; this.userList = data.friends;
for (let i = 0; i < data.friends.length; i++) { for (let i = 0; i < data.friends.length; i++) {
this.userList[i].avatar = data.friends[i].avatar ===""?"https://gep.ljsea.top/tool/file/9f29cc99-1054-4aff-ab37-e7c0016dd1b5.jpeg" : data.friends[i].avatar;
this.hasUnreadMsg[data.friends[i].id] = false; this.hasUnreadMsg[data.friends[i].id] = false;
} }
console.log("avatar: " + this.userList[0].avatar);
for (let i = 0; i < data.groups.length; i++) {
this.hasUnreadMsg["g_" + data.groups[i].ID] = false;
}
this.groupList = data.groups; this.groupList = data.groups;
}, },
filterUsers() { filterUsers() {
@ -360,6 +389,10 @@ export default {
for (let i = 0; i < this.userList.length; i++) { for (let i = 0; i < this.userList.length; i++) {
if (this.userList[i].id === id) { if (this.userList[i].id === id) {
this.cur_user_name = this.userList[i].name; this.cur_user_name = this.userList[i].name;
this.cur_user_avatar =
this.userList[i].avatar === ""
? "https://gep.ljsea.top/tool/file/9f29cc99-1054-4aff-ab37-e7c0016dd1b5.jpeg"
: this.userList[i].avatar;
break; break;
} }
} }
@ -481,21 +514,40 @@ export default {
// //
// console.log("====" + JSON.stringify(msg.data)); // console.log("====" + JSON.stringify(msg.data));
console.log("msg_:", data.data); console.log("msg_:", data.data);
console.log("hasUnreadMsg:", _this.hasUnreadMsg);
let msg_data = JSON.parse(data.data); let msg_data = JSON.parse(data.data);
_this.MsgList.push(msg_data); _this.MsgList.push(msg_data);
if ( if (msg_data.GroupID === 0) {
_this.cur_user_id != msg_data.FromUserID && //msg_data.GroupID === 0,
_this.cur_group_id != msg_data.GroupID if (msg_data.FromUserID !== _this.cur_user_id) {
) { //
if (msg_data.GroupID === 0) {
_this.hasUnreadMsg[msg_data.FromUserID] = true; _this.hasUnreadMsg[msg_data.FromUserID] = true;
} else { //
_this.hasUnreadMsg["g_" + msg_data.GroupID] = true; for (let i = 0; i < _this.userList.length; i++) {
if (_this.userList[i].id === msg_data.FromUserID) {
let temp = _this.userList[i];
_this.userList.splice(i, 1); //
_this.userList.unshift(temp); //
break;
}
}
} }
} else { } else {
_this.scrollToBottom(); if (msg_data.GroupID !== _this.cur_group_id) {
//
_this.hasUnreadMsg["g_" + msg_data.GroupID] = true;
//
for (let i = 0; i < _this.groupList.length; i++) {
if (_this.groupList[i].ID === msg_data.GroupID) {
let temp = _this.groupList[i];
_this.groupList.splice(i, 1); //
_this.groupList.unshift(temp); //
break;
}
}
}
} }
_this.scrollToBottom();
//console.log("msglist:", _this.MsgList); //console.log("msglist:", _this.MsgList);
// //
} else if (data.type == "check") { } else if (data.type == "check") {
@ -624,7 +676,7 @@ export default {
}; };
reader.readAsArrayBuffer(this.file); reader.readAsArrayBuffer(this.file);
} catch (error) { } catch (error) {
console.error('计算MD5值出错:', error); console.error("计算MD5值出错:", error);
} }
} }
}, },
@ -632,7 +684,7 @@ export default {
handleMenuSelect(val) { handleMenuSelect(val) {
router.push(val); router.push(val);
//websocket //websocket
if (this.socket!= null) { if (this.socket != null) {
this.socket.close(); this.socket.close();
} }
}, },
@ -641,67 +693,68 @@ export default {
console.log("file has been selected:", this.file); console.log("file has been selected:", this.file);
}, },
readFileAndCalculateMD5() { readFileAndCalculateMD5() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (event) => { reader.onload = (event) => {
const wordArray = CryptoJS.lib.WordArray.create(event.target.result); const wordArray = CryptoJS.lib.WordArray.create(event.target.result);
const md5Hash = CryptoJS.MD5(wordArray); const md5Hash = CryptoJS.MD5(wordArray);
const md5Str = md5Hash.toString(CryptoJS.enc.Hex); const md5Str = md5Hash.toString(CryptoJS.enc.Hex);
//console.log("onload: " + md5Str); //console.log("onload: " + md5Str);
this.file_md5 = md5Str; this.file_md5 = md5Str;
resolve(md5Str); resolve(md5Str);
}; };
reader.onerror = (error) => { reader.onerror = (error) => {
reject(error); reject(error);
}; };
reader.readAsArrayBuffer(this.file); reader.readAsArrayBuffer(this.file);
}); });
}, },
async sendImageOrVideo(){ async sendImageOrVideo() {
if (this.file == null) { if (this.file == null) {
alert('请先选择要上传的文件'); alert("请先选择要上传的文件");
return; return;
} }
// //
try { try {
let result={}; let result = {};
this.file_md5 = await this.readFileAndCalculateMD5(this.file); this.file_md5 = await this.readFileAndCalculateMD5(this.file);
//console.log("md5:",this.file_md5); //console.log("md5:",this.file_md5);
let md5_result = await GetFileInfoByMd5Service({"md5":this.file_md5,token:this.tokenData.token,"type":1}); let md5_result = await GetFileInfoByMd5Service({
if(md5_result.code === 0){ md5: this.file_md5,
token: this.tokenData.token,
type: 1,
});
if (md5_result.code === 0) {
result = md5_result; result = md5_result;
}else{ } else {
let formData = new FormData(); let formData = new FormData();
formData.append('file', this.file); formData.append("file", this.file);
//console.log("add file: " + this.file); //console.log("add file: " + this.file);
formData.append('upload_type', "1"); formData.append("upload_type", "1");
formData.append('md5', this.file_md5); formData.append("md5", this.file_md5);
formData.append('auth_type', "public"); formData.append("auth_type", "public");
//console.log("formData:",formData); //console.log("formData:",formData);
result = await UploadFileService(formData, this.tokenData.token);
result = await UploadFileService(formData,this.tokenData.token); if (result.code !== 0) {
if (result.code!== 0) { ElMessage.error("上传文件失败,请稍后再试");
ElMessage.error('上传文件失败,请稍后再试'); return;
return; }
}
} }
let resp_data = result.data; let resp_data = result.data;
//console.log("resp:",resp_data); //console.log("resp:",resp_data);
let url = "https://gep.ljsea.top/tool/file/"+resp_data.FileStoreName; let url = "https://gep.ljsea.top/tool/file/" + resp_data.FileStoreName;
let msg_ =""; let msg_ = "";
//markdown //markdown
let fileType = this.file.name.split('.')[1]; let fileType = this.file.name.split(".")[1];
if (!['jpg', 'jpeg', 'png', 'gif', 'mp4'].includes(fileType)) { if (!["jpg", "jpeg", "png", "gif", "mp4"].includes(fileType)) {
//alert(''); //alert('');
msg_ = `文件:[${resp_data.FileName}](${url})`; msg_ = `文件:[${resp_data.FileName}](${url})`;
}else{ } else {
msg_ = `![${resp_data.FileName}](${url})`; msg_ = `![${resp_data.FileName}](${url})`;
}
}
let req = { let req = {
token: localStorage.getItem("token"), token: localStorage.getItem("token"),
@ -729,12 +782,12 @@ export default {
Msg: msg_, Msg: msg_,
CreatedAt: new Date(), CreatedAt: new Date(),
}; };
console.log("msg:",msg); console.log("msg:", msg);
this.MsgList.push(msg); this.MsgList.push(msg);
this.scrollToBottom(); this.scrollToBottom();
this.sendImgDialogVisible = false; this.sendImgDialogVisible = false;
} catch (error) { } catch (error) {
ElMessage.error('上传文件时出现网络错误,请稍后再试'); ElMessage.error("上传文件时出现网络错误,请稍后再试");
console.error(error); console.error(error);
} }
}, },
@ -842,8 +895,8 @@ export default {
} }
.send-image-button-bg { .send-image-button-bg {
background-image: url(../assets/img.jpg); background-image: url(../assets/img.jpg);
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
</style> </style>

View File

@ -10,11 +10,13 @@ import {GetUserInfoService} from "@/api/user.js";
import { GetRedisInfoService } from "@/api/tool.js"; import { GetRedisInfoService } from "@/api/tool.js";
import {DelFGService} from "@/api/user.js"; import {DelFGService} from "@/api/user.js";
import router from "@/router/index.js"; import router from "@/router/index.js";
import { UploadFileService } from "@/api/tool.js";
import {GetFileInfoByMd5Service } from "@/api/tool.js";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { getFriendListService } from "@/api/chat.js"; import { getFriendListService } from "@/api/chat.js";
import {sendMessageService} from "@/api/chat.js"; import {sendMessageService} from "@/api/chat.js";
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import CryptoJS from 'crypto-js';
import Menu from "@/views/Menu.vue"; import Menu from "@/views/Menu.vue";
export default { export default {
@ -30,6 +32,8 @@ export default {
FriendsGRequestList:[], FriendsGRequestList:[],
FriendsTableIsDisplay:false, FriendsTableIsDisplay:false,
RedisIsDisplay:false, RedisIsDisplay:false,
avatar_file: null,
file_md5: "",
RedisList:[], RedisList:[],
FriendsGList:[], FriendsGList:[],
GroupList:[], GroupList:[],
@ -201,6 +205,76 @@ export default {
console.log(e); console.log(e);
} }
}, },
handleAvatarFileUpload(e) {
this.avatar_file = e.target.files[0];
//
if (!this.avatar_file.type.startsWith("image/")) {
ElMessage.error("请选择图片文件");
this.avatar_file = null;
return;
}
//this.UserUpdateForm.avatar = URL.createObjectURL(this.avatar_file);
this.uploadAvatarFile();
},
readFileAndCalculateMD5() {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
const wordArray = CryptoJS.lib.WordArray.create(event.target.result);
const md5Hash = CryptoJS.MD5(wordArray);
const md5Str = md5Hash.toString(CryptoJS.enc.Hex);
//console.log("onload: " + md5Str);
this.file_md5 = md5Str;
resolve(md5Str);
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsArrayBuffer(this.avatar_file);
});
},
async uploadAvatarFile(){
if (this.avatar_file == null) {
alert('请先选择要上传的文件');
return;
}
try {
let result={};
this.file_md5 = await this.readFileAndCalculateMD5(this.avatar_file);
//console.log("md5:",this.file_md5);
let md5_result = await GetFileInfoByMd5Service({"md5":this.file_md5,token:this.tokenData.token,"type":1});
if(md5_result.code === 0){
result = md5_result;
}else{
let formData = new FormData();
formData.append('file', this.avatar_file);
//console.log("add file: " + this.file);
formData.append('upload_type', "1");
formData.append('md5', this.file_md5);
formData.append('auth_type', "public");
//console.log("formData:",formData);
result = await UploadFileService(formData,this.tokenData.token);
if (result.code!== 0) {
ElMessage.error('上传文件失败,请稍后再试');
return;
}
}
let resp_data = result.data;
//console.log("resp:",resp_data);
let url = "https://gep.ljsea.top/tool/file/"+resp_data.FileStoreName;
this.UserUpdateForm.avatar = url;
//
await this.updateUserInfo();
} catch (error) {
ElMessage.error('上传文件时出现网络错误,请稍后再试');
console.error(error);
}
},
async updateUserInfo(){ async updateUserInfo(){
let result ={} let result ={}
try{ try{
@ -296,6 +370,7 @@ export default {
this.UserUpdateForm.run = result.data.Run; this.UserUpdateForm.run = result.data.Run;
this.UserUpdateForm.upload = result.data.Upload; this.UserUpdateForm.upload = result.data.Upload;
this.UserUpdateForm.age = result.data.Age; this.UserUpdateForm.age = result.data.Age;
this.UserUpdateForm.avatar = result.data.Avatar === "" ? "https://gep.ljsea.top/tool/file/9f29cc99-1054-4aff-ab37-e7c0016dd1b5.jpeg":result.data.Avatar;
this.UserUpdateForm.role = result.data.Role; this.UserUpdateForm.role = result.data.Role;
this.UserUpdateForm.gender = result.data.Gender; this.UserUpdateForm.gender = result.data.Gender;
this.UserUpdateForm.create_time = result.data.CreatedAt; this.UserUpdateForm.create_time = result.data.CreatedAt;
@ -309,6 +384,7 @@ export default {
localStorage.setItem("video_func",result.data.VideoFunc); localStorage.setItem("video_func",result.data.VideoFunc);
localStorage.setItem("device_func",result.data.DeviceFunc); localStorage.setItem("device_func",result.data.DeviceFunc);
localStorage.setItem("cid_func",result.data.CIDFunc); localStorage.setItem("cid_func",result.data.CIDFunc);
localStorage.setItem("avatar",result.data.Avatar);
//console.log("my role:",this.role); //console.log("my role:",this.role);
} }
} }
@ -442,6 +518,11 @@ export default {
autocomplete="on" autocomplete="on"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="头像" prop="name">
<el-avatar :size="80" :src="UserUpdateForm.avatar"></el-avatar>
<!-- 选择图片 -->
<input type="file" @change="handleAvatarFileUpload">更换头像</input>
</el-form-item>
<el-form-item label="用户名" prop="name"> <el-form-item label="用户名" prop="name">
<el-input <el-input
v-model="UserUpdateForm.name" v-model="UserUpdateForm.name"