video_ca/src/views/Im.vue

363 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div style="padding: 10px; margin-bottom: 50px">
<el-button
type="primary"
size="mini"
@click.prevent="handleMenuSelect('/User')"
>用户</el-button
>
<el-row v-loading="loading" element-loading-text="正在连接....">
<el-col :span="16">
<div
style="
width: 800px;
margin: 0 auto;
background-color: white;
border-radius: 5px;
box-shadow: 0 0 10px #ccc;
"
>
<div style="text-align: center; line-height: 50px">
{{ chatUser }}
</div>
<div
style="height: 350px; overflow: auto; border-top: 1px solid #ccc"
v-html="content"
></div>
<div style="height: 200px">
<textarea
v-model="text"
style="
height: 160px;
width: 100%;
padding: 20px;
border: none;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
outline: none;
"
></textarea>
<div style="text-align: right; padding-right: 10px">
<el-button type="primary" size="mini" @click="send"
>发送</el-button
>
</div>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import { ref, onMounted, inject, onUnmounted } from "vue";
import { getImKeyService } from "@/api/im.js";
import router from "@/router/index.js";
import * as crypto from "crypto";
import CryptoJS from "crypto-js";
import { ElLoading } from "element-plus";
import Cookies from "js-cookie";
export default {
data() {
return {
circleUrl:
"https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
user: {},
isCollapse: false,
users: [{ username: "admin" }, { username: "zhang" }],
chatUser: "2002",
text: "",
messages: [],
session: "",
loading: ref(true),
imKey: "testimkey",
socket: null,
content: "",
to_user_id: 0,
to_user_name: "",
cnt: 0,
intervalId: null,
tokenData: {
token: localStorage.getItem("token"),
ip: localStorage.getItem("ip"),
userId: localStorage.getItem("userId"),
username: localStorage.getItem("username"),
to_user_id: this.to_user_id,
},
intervalId: ref(null),
timerId: null, // 用于存储定时器的ID
};
},
methods: {
async send() {
if (!this.text) {
this.$message({ type: "warning", message: "请输入内容" });
} else {
if (typeof WebSocket == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
var aesEnc = await this.aesEncrypt(this.text, this.imKey, this.imKey);
let data =
'{"type":"msg","data":"' +
aesEnc +
'","to_user_id":' +
this.to_user_id +
',"from_user_id":' +
this.tokenData.userId +
',"session":"' +
this.session +
'"}';
this.socket.send(data); // 将组装好的json发送给服务端由服务端进行转发
this.messages.push(JSON.parse(data));
// 构建消息内容,本人消息
this.createContent(null, this.tokenData.username, this.text);
this.text = "";
}
}
},
startInterval() {
this.timerId = setInterval(() => {
this.getIMKey();
}, 1000); // 每秒1000毫秒执行一次
},
handleMenuSelect(val) {
router.push(val);
},
stopInterval() {
clearInterval(this.timerId);
},
async getIMKey() {
let result = {};
try {
let req = {
to_user_id: this.to_user_id,
token: this.tokenData.token,
};
result = await getImKeyService(req);
} catch (e) {
console.log(e);
}
let data = result.data;
if (result.code != 0) {
this.cnt++;
if (this.cnt > 10) {
//暂停定时器
this.stopInterval();
alert("连接失败,请重试!");
router.push("/user");
}
return;
}
try {
if (data.is_read == 1) {
// 读取到了新的消息
this.imKey = data.im_key;
this.session = data.im_session;
localStorage.setItem("imkey_" + this.to_user_id, this.imKey);
//记录过期时间
localStorage.setItem(
"imkey_" + this.to_user_id + "_expire",
data.expire
);
this.session = data.im_session;
this.loading=false
//暂停定时器
this.stopInterval();
this.connectWebSocket();
} else {
this.cnt++;
if (this.cnt > 30) {
//暂停定时器
this.stopInterval();
confirm("连接失败,请重试!");
router.push("/user");
}
}
} catch (e) {
console.log(e);
}
},
createContent(remoteUser, nowUser, text) {
// 这个方法是用来将 json的聊天消息数据转换成 html的。
var html;
// 当前用户消息
if (nowUser) {
// nowUser 表示是否显示当前用户发送的聊天消息,绿色气泡
html =
'<div class="el-row" style="padding: 5px 0">\n' +
' <div class="el-col el-col-22" style="text-align: right; padding-right: 10px">\n' +
' <div class="tip left">' +
text +
"</div>\n" +
" </div>\n" +
' <div class="el-col el-col-2">\n' +
' <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +
' <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" style="object-fit: cover;">\n' +
" </span>\n" +
" </div>\n" +
"</div>";
} else if (remoteUser) {
// remoteUser表示远程用户聊天消息蓝色的气泡
html =
'<div class="el-row" style="padding: 5px 0">\n' +
' <div class="el-col el-col-2" style="text-align: right">\n' +
' <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +
' <img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" style="object-fit: cover;">\n' +
" </span>\n" +
" </div>\n" +
' <div class="el-col el-col-22" style="text-align: left; padding-left: 10px">\n' +
' <div class="tip right">' +
text +
"</div>\n" +
" </div>\n" +
"</div>";
}
this.content += html;
},
aesEncrypt(text, password, iv) {
// CryptoJS使用WordArray而不是Buffer所以我们需要将密码和IV转换为WordArray
const passwordWordArray = CryptoJS.enc.Utf8.parse(password);
const ivWordArray = CryptoJS.enc.Utf8.parse(iv);
// 使用CryptoJS创建AES加密实例
const cipher = CryptoJS.AES.encrypt(
CryptoJS.enc.Utf8.parse(text), // 加密的数据
passwordWordArray, // 密钥
{
iv: ivWordArray, // 初始化向量
mode: CryptoJS.mode.CBC, // 加密模式
padding: CryptoJS.pad.Pkcs7, // 填充方式
}
);
// CryptoJS默认返回的是CipherParams对象我们需要将其转换为Base64字符串
const encrypted = cipher.toString();
return encrypted;
},
decryptAES(encryptedData, secretKey, iv) {
// 确保密钥和偏移量IV是WordArray对象
const key = CryptoJS.enc.Utf8.parse(secretKey);
const ivData = CryptoJS.enc.Utf8.parse(iv);
// Base64 解码加密数据
// 注意CryptoJS.enc.Base64.parse 实际上是用于将WordArray转换为Base64字符串的
// 对于从Base64字符串解码应该使用 CryptoJS.enc.Base64.parse(CryptoJS.enc.Base64.stringify(someWordArray))
// 但在这里我们直接使用字符串作为输入因为encryptedData已经是Base64编码的字符串
try {
const decrypted = CryptoJS.AES.decrypt(encryptedData, key, {
iv: ivData,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
});
// 检查解密是否成功
if (decrypted.sigBytes === 0) {
throw new Error("Decryption failed. Invalid data or password.");
}
// 将解密后的 WordArray 转换为 UTF-8 字符串
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
return decryptedText;
} catch (error) {
// 捕获并处理异常
console.error("Decryption error:", error);
throw error; // 或者返回一个错误消息或空字符串等
}
},
connectWebSocket() {
// 连接WebSocket
let _this = this;
if (typeof WebSocket == "undefined") {
console.log("浏览器不支持WebSocket");
} else {
console.log("浏览器支持WebSocket");
let socketUrl =
"wss://gep.ljsea.xyz/im/ws?to_user_id=" +
this.to_user_id +
"&token=" +
this.tokenData.token;
// console.log("socketUrl:", socketUrl);
if (this.socket != null) {
this.socket.close();
this.socket = null;
}
// 开启一个websocket服务
this.socket = new WebSocket(socketUrl);
//打开事件
this.socket.onopen = function () {
this.loading=false
alert("连接成功");
};
this.socket.onerror = (error) => {
console.error("WebSocket Error:", error);
};
//关闭事件
this.socket.onclose = function () {
alert("连接已关闭!");
router.push("/user");
};
// 浏览器端收消息,获得从服务端发送过来的文本消息
this.socket.onmessage = async function (msg) {
//console.log("收到数据====" + msg.data);
let data = JSON.parse(msg.data); // 对收到的json数据进行解析 类似这样的:
console.log("收到数据====" + data);
// 如果服务器端发送过来的json数据
if (data.type == "msg") { // 如果是消息类型,解密消息内容
data.data = await _this.decryptAES(
data.data,
_this.imKey,
_this.imKey
);
_this.messages.push(data);
//console.log("收到数据====" + msg.data);
// 构建消息内容
_this.createContent(_this.to_user_name, null, data.data);
}else if (data.type == "offline"){
alert("对方已下线");
this.socket.close();
router.push("/user");
}
};
}
},
},
// 生命周期钩子,在组件挂载完成后被调用
mounted() {
this.to_user_id = localStorage.getItem("to_user_id");
this.to_user_name = localStorage.getItem("to_user_name");
this.chatUser = this.to_user_name;
//console.log("to_user_id:", this.to_user_id, this.to_user_name);
this.startInterval();
//setTimeout(() => this.loading=false, 3000);
//this.connectWebSocket();
},
// 生命周期钩子,在组件卸载之前被调用
onUnmounted() {
this.stopInterval();
this.socket.close();
this.loading = false;
},
};
</script>
<style>
.tip {
color: white;
text-align: center;
border-radius: 10px;
font-family: sans-serif;
padding: 10px;
width: auto;
display: inline-block !important;
display: inline;
}
.right {
background-color: deepskyblue;
}
.left {
background-color: forestgreen;
}
</style>