添加chat功能

This commit is contained in:
539943419 committed 2024-03-22 14:35:46 +08:00
1 parent 08e7820f37
commit 127f58bd36
13 files changed
+661 -218

No files matched your search

+87 -17
View File
@@ -1,8 +1,50 @@
<script setup>
import uploadFileMiao from "./components/uploadFileMiao.vue";
import downloadFileMiao from "./components/downloadFileMiao.vue";
import settingMiao from "./components/setting.vue";
import {NNotificationProvider,NMessageProvider,NCard,NTabs,NTabPane} from 'naive-ui'
import upload from "./views/uploadFileMiao.vue";
import download from "./views/downloadFileMiao.vue";
import setting from "./views/setting.vue";
import chat from "./views/chat.vue";
import { NNotificationProvider, NMessageProvider, NCard, NMenu, NIcon } from 'naive-ui'
import { ref, watch, h ,shallowRef} from "vue";
// import { RouterView, useRouter } from "vue-router";
import {
ArrowUpCircleOutline as uploadIcon,
ArrowDownCircleOutline as downloadIcon,
ChatboxEllipsesOutline as chatIcon,
SettingsOutline as settingIcon
} from '@vicons/ionicons5'
const activePage = ref('upload')
const isComponent = shallowRef(upload)
watch(activePage, () => {
isComponent.value={upload,download,setting,chat}[activePage.value]
})
function renderIcon(icon) {
return () => h(NIcon, null, { default: () => h(icon) })
}
const menuOptions = [
{
label: '上传',
key: 'upload',
icon: renderIcon(uploadIcon),
},
{
label: '下载',
key: 'download',
icon: renderIcon(downloadIcon),
},
{
label: 'Chat',
key: 'chat',
icon: renderIcon(chatIcon),
},
{
label: '设置',
key: 'setting',
icon: renderIcon(settingIcon),
}
]
</script>
@@ -12,17 +54,35 @@ import {NNotificationProvider,NMessageProvider,NCard,NTabs,NTabPane} from 'naive
<n-notification-provider placement="bottom-right" :max="3">
<n-message-provider>
<n-card content-style="padding: 0; margin:0;" id="theCard">
<n-tabs type="line" size="large" :tabs-padding="20" pane-style="padding: 20px;">
<n-tab-pane name="上传文件">
<uploadFileMiao style />
</n-tab-pane>
<n-tab-pane name="下载文件">
<downloadFileMiao style/>
</n-tab-pane>
<n-tab-pane name="设置">
<settingMiao style="max-width:100%" />
</n-tab-pane>
</n-tabs>
<div id="menu">
<n-menu mode="horizontal" :responsive="true" v-model:value="activePage" :options="menuOptions" :icon-size="26"></n-menu>
</div>
<div style="padding: 20px;">
<KeepAlive>
<component :is="isComponent"></component>
</KeepAlive>
<!-- <router-view v-slot="{ Component }">
<keep-alive :include=['uploadFileMiao']>
<component :is="Component" :key="$route.fullPath"/>
</keep-alive>
</router-view> -->
</div>
<!-- <n-tabs type="line" size="large" :tabs-padding="20" pane-style="padding: 20px;"
display-directive="show">
<n-tab-pane name="上传文件">
<uploadFileMiao style />
</n-tab-pane>
<n-tab-pane name="下载文件">
<downloadFileMiao style />
</n-tab-pane>
<n-tab-pane name="miaoChat">
<chat style />
</n-tab-pane>
<n-tab-pane name="设置">
<settingMiao style="max-width:100%" />
</n-tab-pane>
</n-tabs> -->
</n-card>
</n-message-provider>
</n-notification-provider>
@@ -39,9 +99,19 @@ import {NNotificationProvider,NMessageProvider,NCard,NTabs,NTabPane} from 'naive
justify-content: center;
align-items: center;
}
#theCard {
width: 50%;
height: 80%;
width: 40vw;
height: 90vh;
}
#menu{
width: 100%;
height: auto;
/* background-color: blue; */
padding-top: 5px;
border-bottom: 1px solid rgb(60, 60, 60, 0.1);
user-select: none
}
@media screen and (max-width:768px) {
#theCard {
+9 -12
View File
@@ -18,14 +18,15 @@ const blob2Uint8Array = function (blob) {
});
};
const decrypt = (key, data) => {
key = key.trim()
return blob2Uint8Array(data).then(data => {
// 无加密
if (key.length === 0) {
return data;
}
// SM4
if (key.length >= 34) {
key = key.substring(3);
if (key.length === 19) {
key = [...aesjs.utils.utf8.toBytes(key.substring(3))];
// console.log([...data])
var decryptedBytes = new Uint8Array(sm4.decrypt([...data], key, { output: "array" }));
return decryptedBytes;
@@ -39,14 +40,17 @@ const decrypt = (key, data) => {
});
};
const decryptString = (key, str) => {
key = key.trim()
console.log(key.length)
// 无加密
if (key.length === 0) {
return sm4.decrypt(str, "miaospringmiaospringmiaospringao");
// return sm4.decrypt(str, "miaospringmiaospringmiaospringao");
return Base64.decode(str)
}
// SM4
// console.log(key,key.length)
if (key.length >= 34) {
key = key.substring(3);
if (key.length === 19) {
key = [...aesjs.utils.utf8.toBytes(key.substring(3))];
return sm4.decrypt(str, key);
}
// AES
@@ -67,13 +71,6 @@ const downloadFromUint8Array = (data, fileName) => {
let blob = new Blob([uint8Array], { type: "text/plain" });
// 使用file-saver.js
saveAs(blob, fileName);
//let url = URL.createObjectURL(blob);
//let a = document.createElement("a");
//a.href = url;
//a.download = fileName;
//a.click();
//URL.revokeObjectURL(url);
};
export { blob2Uint8Array, decrypt, decryptString, downloadFromUint8Array };
+15 -5
View File
@@ -21,13 +21,17 @@ const generateKey = function(setting) {
}
}
var returnStr = "",
length = setting.activeFuction === "AES128" ? 16 : 32
length = setting.activeFuction !== "AES256" ? 16 : 32
// charMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (var i = 0; i < length; i++) {
var index = Math.round(Math.random() * charMap.length);
var index = parseInt(Math.random() * charMap.length);
returnStr += charMap.substring(index, index + 1);
}
if(setting.activeFuction === "SM4"){
returnStr = 'SM4' + returnStr
}
// console.log(returnStr,returnStr.length)
return returnStr;
};
const blob2Uint8Array = function(blob) {
@@ -61,12 +65,15 @@ const encrypt = (key, data, fileName, setting, message) => {
let encrypted_data = null
if(setting.activeFuction === "不加密"){
encrypted_data = data
// AES
}else if(setting.activeFuction != "SM4"){
message.info(`开始加密${fileName}`);
encrypted_data = AES(key,data)
message.info(`${fileName}加密完成`);
// SM4
}else{
message.info(`开始加密${fileName}`);
key = [...aesjs.utils.utf8.toBytes(key.substring(3))];
encrypted_data = SM4(key,data)
message.info(`${fileName}加密完成`);
}
@@ -76,9 +83,10 @@ const encrypt = (key, data, fileName, setting, message) => {
// setting为pinia存储的setting。。。
const encryptString = (key, str, setting) => {
// console.log(key)
if(setting.activeFuction === "不加密"){
return sm4.encrypt(str, 'miaospringmiaospringmiaospringao')
// console.log(key,key.length)
if(setting.activeFuction === "不加密" || key === ''){
// return sm4.encrypt(str, 'miaospringmiaospringmiaospringao')
return Base64.encode(str)
}else if(setting.activeFuction !== "SM4"){
key = aesjs.utils.utf8.toBytes(key);
var data = aesjs.utils.utf8.toBytes(str);
@@ -86,6 +94,8 @@ const encryptString = (key, str, setting) => {
var encryptedBytes = aesCtr.encrypt(data);
return Base64.encode(encryptedBytes);
}else{
key = [...aesjs.utils.utf8.toBytes(key.substring(3))];
// console.log(key)
return sm4.encrypt(str, key)
}
};
+2 -2
View File
@@ -3,7 +3,7 @@ import 'vfonts/Lato.css'
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
// import router from "./router";
// import naive from "naive-ui";
import { createPinia } from "pinia";
@@ -14,7 +14,7 @@ import { createPinia } from "pinia";
const pinia = createPinia()
const app = createApp(App);
app.use(router);
// app.use(router);
app.use(pinia);
// app.use(naive);
+42 -14
View File
@@ -1,22 +1,50 @@
// 未使用
import { createRouter, createWebHistory } from 'vue-router'
// import HomeView from '../views/HomeView.vue'
import uploadFileMiao from "../views/uploadFileMiao.vue";
import downloadFileMiao from "../views/downloadFileMiao.vue";
import settingMiao from "../views/setting.vue";
import chat from "../views/chat.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
// {
// path: '/',
// name: 'home',
// component: HomeView
// },
// {
// path: '/about',
// name: 'about',
// // route level code-splitting
// // this generates a separate chunk (About.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import('../views/AboutView.vue')
// }
{
path: '/',
redirect: '/upload',
},
{
path: '/upload',
name: 'upload',
component: uploadFileMiao,
meta: {
keepAlive: true,
}
},
{
path: '/download',
name: 'download',
component: downloadFileMiao,
meta: {
keepAlive: true,
}
},
{
path: '/setting',
name: 'setting',
component: settingMiao,
meta: {
keepAlive: true,
}
},
{
path: '/chat',
name: 'chat',
component: chat,
meta: {
keepAlive: true,
}
},
]
})
+139
View File
@@ -0,0 +1,139 @@
<template>
<n-button @click="createGroup">
createTest
</n-button>
<n-text> createTest gID:{{ gID }}<br />key:{{ key }}<br />code:{{ gID+key }}</n-text>
<n-input
v-model:value="codeInput"
type="textarea"
placeholder="测试加入群组"
/>
<n-button @click="joinGroup(codeInput)">
joinTest
</n-button>
<n-input
v-model:value="msg"
type="textarea"
placeholder="测试发送信息"
/>
<n-button @click="sendMsg(codeInput.substring(0,8),msg)">
sendMsgTest
</n-button>
<n-button @click="data.length = 0">
clearMsg
</n-button>
<n-data-table
:columns="columns"
:data="data"
:row-props="null"
:max-height="250"
:bordered="false"
:single-line="false"
/>
</template>
<script setup>
import {ref} from 'vue'
import {NText,NDataTable,NButton,NInput} from 'naive-ui'
import { useSettingStore } from "@/pinia/store";
import { generateKey,encryptString } from '@/func/upload';
import { decryptString } from '@/func/download';
const setting = useSettingStore();
const serverPort = setting.serverPort;
const baseUrl = 'ws' + "://" + location.hostname + ":" + serverPort;
const columns = [
{
title: "from",
key: "from"
},
{
title: "message",
key: "msg"
},
];
const data = ref([])
const gID = ref('')
const codeInput = ref('')
const key = ref('')
const msg = ref('')
const joinedGroup = []
const gID2key = {}
const socket = new WebSocket(baseUrl);
socket.addEventListener("open", (event) => {
// socket.send("");
});
socket.addEventListener("message", (event) => {
const msg = JSON.parse(event.data)
data.value.push({from:'server',msg:event.data})
if(msg.type){
let type = msg.type
if(type === 'sMsg'){
let type1 = msg.type1
if(type1 === 'create'){
gID.value=msg.gID
}else if(type1 === 'joinVerify'){
let gID = msg.gID
let key = gID2key[gID]
console.log(gID2key,gID,key)
let res = decryptString(key,msg.verify)
console.log(res,key,msg.verify)
if(res === 'miaoSpring'){
socket.send(JSON.stringify({type:'join',type1:'success',gID}))
joinedGroup.push(msg.gID)
}
}else if(type1 === 'history'){
let gID = msg.gID
let key = gID2key[gID]
for(let m of JSON.parse(msg.history)){
data.value.push({from:'decrypt',msg:decryptString(key,m.msg)})
}
}
}else if(type === 'gMsg'){
let gID = msg.gID
let key = gID2key[gID]
data.value.push({from:'decrypt',msg:decryptString(key,msg.msg)})
}
}
});
function createGroup(){
let keyt = generateKey(setting)
key.value = keyt
socket.send(JSON.stringify({type:'create',verify:encryptString(keyt,'miaoSpring',setting)}))
}
function joinGroup(code){
var gID = code.substring(0, 8);
var key = code.substring(8);
if(joinedGroup.includes(gID)){
return
}
gID2key[gID] = key
joinGroup_Verify(gID)
}
function joinGroup_Verify(gID){
socket.send(JSON.stringify({type:'join',type1:'verify',gID}))
}
function sendMsg(gID,msg){
let key = gID2key[gID]
msg = encryptString(key,msg,setting)
socket.send(JSON.stringify({type:'message',msg,gID}))
}
</script>
<style>
</style>
@@ -22,6 +22,7 @@
:max-height="250"
:bordered="false"
:single-line="false"
style="user-select: none;"
/>
</div>
</template>
@@ -2,7 +2,7 @@
<div>
<n-list hoverable>
<n-list-item>
<p style="font-size:larger;position:absolute;left:5px;">选择加密算法:</p>
<p style="font-size:larger;position:absolute;left:5px;user-select: none;">选择加密算法:</p>
<br />
<!-- <br /> -->
<!-- <br /> -->
@@ -16,7 +16,7 @@
</n-flex>
</n-list-item>
<n-list-item>
<p style="font-size:larger;position:absolute;left:5px;">选择密钥组成:</p>
<p style="font-size:larger;position:absolute;left:5px;user-select: none;">选择密钥组成:</p>
<br />
<n-flex justify="end">
<span v-for="k in keyButtons" style="margin:5px">
@@ -11,8 +11,8 @@
<!-- <div style="margin-bottom: 12px">
<n-icon size="48" :depth="3"></n-icon>
</div>-->
<n-text style="font-size: 16px">点击此处或者拖动文件到该区域来上传</n-text>
<n-p depth="3" style="margin: 8px 0 0 0">
<n-text style="font-size: 16px;user-select: none;">点击此处或者拖动文件到该区域来上传</n-text>
<n-p depth="3" style="margin: 8px 0 0 0;user-select: none;">
最大支持200MB大小文件,其实只是懒得做分片上传而已
<br />虽然理论上没有人有能力审查你上传的东西
<br />反正没人用写这么多干什么。。。
@@ -31,6 +31,7 @@
:max-height="250"
:bordered="false"
:single-line="false"
style="user-select: none;"
/>
</div>
</template>
@@ -208,9 +209,9 @@ const customRequest = ({
if (xhr.readyState === 4 && xhr.status === 200) {
let id = xhr.responseText;
let code = id + key
if(setting.activeFuction === "SM4"){
code = id + 'SM4' + key
}
// if(setting.activeFuction === "SM4"){
// code = id + 'SM4' + key
// }
file_Code_Map.value.push({
code: code,
fileName: fileName