添加登录功能
This commit is contained in:
1 parent
20e9f4940e
commit
530e54a596
17 files changed
+632
-160
No files matched your search
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<n-button @click="signOut" type="error">
|
||||
退出登录
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import { NCheckbox, NInputGroupLabel, NInputGroup, NP, NFlex, NDataTable, NButton, NInput, NCard } from 'naive-ui'
|
||||
|
||||
function signOut(){
|
||||
const items = ['token']
|
||||
items.forEach(v=>{
|
||||
localStorage.removeItem(v)
|
||||
})
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
+117
-71
@@ -1,15 +1,17 @@
|
||||
<template>
|
||||
<n-flex style="padding-top: 10px;padding-bottom: 10px;">
|
||||
<n-input v-model:value="groupCode" type="text" placeholder="输入进群码" style="flex:1">
|
||||
<n-input v-model:value="groupCode" type="text" placeholder="输入邀请码" style="flex:1">
|
||||
<template #suffix>
|
||||
<n-icon :component="CopyOutline" style="cursor: pointer;" @click='toClipboard(groupCode)' />
|
||||
</template>
|
||||
</n-input>
|
||||
<n-flex>
|
||||
<n-button @click="createGroup" :disabled="groupCode !== ''" :type="groupCode !== ''?'default':'primary'" ghost >
|
||||
<n-button @click="createGroup" :disabled="groupCode !== ''" :type="groupCode !== '' ? 'default' : 'primary'"
|
||||
ghost>
|
||||
创建群组
|
||||
</n-button>
|
||||
<n-button @click="joinGroup(groupCode)" :disabled="groupCode === ''" :type="groupCode === ''?'default':'primary'" ghost>
|
||||
<n-button @click="joinGroup(groupCode)" :disabled="groupCode === ''"
|
||||
:type="groupCode === '' ? 'default' : 'primary'" ghost>
|
||||
加入群组
|
||||
</n-button>
|
||||
</n-flex>
|
||||
@@ -23,54 +25,65 @@
|
||||
<n-button @click="joinGroup(codeInput)">
|
||||
joinTest
|
||||
</n-button> -->
|
||||
<n-input
|
||||
v-model:value="msg"
|
||||
type="textarea"
|
||||
placeholder="测试发送信息"
|
||||
:autosize="{
|
||||
minRows: 3,
|
||||
maxRows: 3
|
||||
}"
|
||||
/>
|
||||
<n-input v-model:value="msg" type="textarea" placeholder="测试发送信息" :autosize="{
|
||||
minRows: 3,
|
||||
maxRows: 3
|
||||
}" />
|
||||
<n-flex style="padding-top: 10px;padding-bottom: 10px;">
|
||||
<n-select v-model:value="activeGID" :options="joinedGroup.map(gid=>{return {label:gid,value:gid}})" placeholder="选择当前查看群组"
|
||||
style="flex:1"/>
|
||||
<n-button @click="sendMsg(activeGID,msg)" :disabled="msg === '' || activeGID === null" :type="msg === '' || activeGID === null?'default':'primary'">
|
||||
<n-select v-model:value="activeGID" :options="joinedGroup.map(gid => { return { label: gid, value: gid } })"
|
||||
placeholder="选择当前查看群组" style="flex:1" />
|
||||
<n-button @click="sendMsg(activeGID, msg)" :disabled="msg === '' || activeGID === null"
|
||||
:type="msg === '' || activeGID === null ? 'default' : 'primary'">
|
||||
发送消息
|
||||
</n-button>
|
||||
<n-button @click="shareWithQR()" :disabled="activeGID === null"
|
||||
:type="activeGID === null ? 'default' : 'primary'">
|
||||
分享群组
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<!-- <n-button @click="dataTest.length = 0">
|
||||
clearMsg
|
||||
</n-button> -->
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:data="activeGID?Messages[activeGID].map(v=>{v.timestampString = new Date(v.timestamp).toLocaleString();return v}):[]"
|
||||
:row-props="null"
|
||||
<n-data-table :columns="columns"
|
||||
:data="activeGID ? Messages[activeGID].map(v => { v.timestampString = new Date(v.timestamp).toLocaleString(); return v }).reverse() : []"
|
||||
:row-props="null" :bordered="false" :single-line="false" :max-height="400" />
|
||||
|
||||
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: fit-content;"
|
||||
title=""
|
||||
:bordered="false"
|
||||
:single-line="false"
|
||||
:max-height="400"
|
||||
/>
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-qr-code :value="QRCodeData" style="padding: 0;" :size="256" error-correction-level="Q" :v-if="showModal" :icon-src='QRIconUrl' :icon-size="128"/>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from 'vue'
|
||||
import { useNotification } from "naive-ui";
|
||||
import { ref,onMounted } from 'vue'
|
||||
// import { useModal } from "naive-ui";
|
||||
import useClipboard from "vue-clipboard3";
|
||||
import {NIcon,NFlex,NDataTable,NButton,NInput,NSelect} from 'naive-ui'
|
||||
import { NIcon, NFlex, NDataTable, NButton, NInput, NSelect,NModal,NCard , NQrCode} from 'naive-ui'
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import { generateKey,encryptString } from '@/func/upload';
|
||||
import { generateKey, encryptString } from '@/func/upload';
|
||||
import { decryptString } from '@/func/download';
|
||||
import WebsocketHeartbeatJs from 'websocket-heartbeat-js';
|
||||
import {CopyOutline} from '@vicons/ionicons5'
|
||||
import { CopyOutline } from '@vicons/ionicons5'
|
||||
import getQueryString from '@/func/getQuery'
|
||||
|
||||
|
||||
|
||||
const notification = useNotification();
|
||||
// const modal = useModal();
|
||||
const { toClipboard } = useClipboard();
|
||||
|
||||
const setting = useSettingStore();
|
||||
const serverPort = setting.serverPort;
|
||||
const baseUrl = 'ws' + "://" + location.hostname + ":" + serverPort;
|
||||
const QRIconUrl = ref(location.protocol + "//" + location.hostname + ":" + serverPort + "/favicon.ico")
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -110,6 +123,15 @@ const columnsTest = [
|
||||
},
|
||||
];
|
||||
|
||||
// 用于二维码分享
|
||||
const showModal = ref(false)
|
||||
const QRCodeData = ref('')
|
||||
function shareWithQR(){
|
||||
let code = activeGID.value+gID2key[activeGID.value].key
|
||||
QRCodeData.value = location.protocol + "//" + location.hostname + ":" + serverPort + `/?aP=chat&cG=${code}`
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
// 凭此code进入群组
|
||||
const groupCode = ref('')
|
||||
|
||||
@@ -136,10 +158,10 @@ const gID2key = {}
|
||||
|
||||
const options = {
|
||||
url: baseUrl,
|
||||
pingTimeout: 20000,
|
||||
pongTimeout: 10000,
|
||||
pingTimeout: 20000,
|
||||
pongTimeout: 10000,
|
||||
reconnectTimeout: 3000,
|
||||
pingMsg: JSON.stringify({type:'heartbeat'})
|
||||
pingMsg: JSON.stringify({ type: 'heartbeat' })
|
||||
}
|
||||
let socket = new WebsocketHeartbeatJs(options);
|
||||
|
||||
@@ -150,64 +172,64 @@ let socket = new WebsocketHeartbeatJs(options);
|
||||
// setInterval(()=>console.log(activeGID.value),1000)
|
||||
|
||||
|
||||
socket.onmessage= (event) => {
|
||||
socket.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data)
|
||||
dataTest.value.push({from:'server',msg:event.data})
|
||||
if(msg.type){
|
||||
dataTest.value.push({ from: 'server', msg: event.data })
|
||||
if (msg.type) {
|
||||
let type = msg.type
|
||||
if(type === 'sMsg'){
|
||||
if (type === 'sMsg') {
|
||||
let type1 = msg.type1
|
||||
if(type1 === 'create'){
|
||||
gID.value=msg.gID
|
||||
if (type1 === 'create') {
|
||||
gID.value = msg.gID
|
||||
groupCode.value = msg.gID + key.value
|
||||
joinGroup(groupCode.value)
|
||||
}else if(type1 === 'joinVerify'){
|
||||
} else if (type1 === 'joinVerify') {
|
||||
let gID = msg.gID
|
||||
let key = gID2key[gID].key
|
||||
let res = decryptString(key,msg.verify)
|
||||
console.log(res,key,msg.verify)
|
||||
let res = decryptString(key, msg.verify)
|
||||
// console.log(res, key, msg.verify)
|
||||
|
||||
// 验证成功,正式加入群组
|
||||
if(res === 'miaoSpring'){
|
||||
socket.send(JSON.stringify({type:'join',type1:'success',gID}))
|
||||
if (res === 'miaoSpring') {
|
||||
socket.send(JSON.stringify({ type: 'join', type1: 'success', gID }))
|
||||
joinedGroup.value.push(msg.gID)
|
||||
Messages.value[msg.gID] = []
|
||||
if(activeGID.value === null){
|
||||
activeGID.value = msg.gID
|
||||
}
|
||||
// if(activeGID.value === null){
|
||||
activeGID.value = msg.gID
|
||||
// }
|
||||
}
|
||||
}else if(type1 === 'history'){
|
||||
} else if (type1 === 'history') {
|
||||
let gID = msg.gID
|
||||
let key = gID2key[gID].key
|
||||
Messages.value[msg.gID].length = 0
|
||||
for(let m of JSON.parse(msg.history)){
|
||||
Messages.value[msg.gID].push({type:m.type,msg:decryptString(key,m.msg),from:m.from,timestamp:m.timestamp})
|
||||
dataTest.value.push({from:'decrypt',msg:decryptString(key,m.msg)})
|
||||
for (let m of JSON.parse(msg.history)) {
|
||||
Messages.value[msg.gID].push({ type: m.type, msg: decryptString(key, m.msg), from: m.from, timestamp: m.timestamp })
|
||||
dataTest.value.push({ from: 'decrypt', msg: decryptString(key, m.msg) })
|
||||
}
|
||||
}else if(type1 === 'uuid'){
|
||||
} else if (type1 === 'uuid') {
|
||||
uuid.value = msg.uuid
|
||||
}
|
||||
}else if(type === 'gMsg'){
|
||||
} else if (type === 'gMsg') {
|
||||
let gID = msg.gID
|
||||
let key = gID2key[gID].key
|
||||
Messages.value[msg.gID].push({type:msg.type,msg:decryptString(key,msg.msg),from:msg.from,timestamp:msg.timestamp})
|
||||
Messages.value[msg.gID].push({ type: msg.type, msg: decryptString(key, msg.msg), from: msg.from, timestamp: msg.timestamp })
|
||||
// console.log(Messages.value)
|
||||
dataTest.value.push({from:'decrypt',msg:decryptString(key,msg.msg)})
|
||||
dataTest.value.push({ from: 'decrypt', msg: decryptString(key, msg.msg) })
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
socket.onopen = ()=>{
|
||||
if(uuid.value){
|
||||
socket.send(JSON.stringify({type:'uuid',type1:'set',uuid:uuid.value}))
|
||||
}else{
|
||||
socket.send(JSON.stringify({type:'uuid',type1:'get'}))
|
||||
socket.onopen = () => {
|
||||
if (uuid.value) {
|
||||
socket.send(JSON.stringify({ type: 'uuid', type1: 'set', uuid: uuid.value }))
|
||||
} else {
|
||||
socket.send(JSON.stringify({ type: 'uuid', type1: 'get' }))
|
||||
}
|
||||
// console.log(joinedGroup)
|
||||
let toJoin = [...joinedGroup.value]
|
||||
joinedGroup.value.length = 0
|
||||
for(let gID of toJoin){
|
||||
let code = gID+gID2key[gID].key
|
||||
for (let gID of toJoin) {
|
||||
let code = gID + gID2key[gID].key
|
||||
console.log(code)
|
||||
joinGroup(code)
|
||||
}
|
||||
@@ -217,16 +239,16 @@ socket.onreconnect = (e) => {
|
||||
console.log('reconnecting...');
|
||||
}
|
||||
|
||||
function createGroup(){
|
||||
function createGroup() {
|
||||
let keyt = generateKey(setting)
|
||||
key.value = keyt
|
||||
socket.send(JSON.stringify({type:'create',verify:encryptString(keyt,'miaoSpring',setting)}))
|
||||
socket.send(JSON.stringify({ type: 'create', verify: encryptString(keyt, 'miaoSpring', setting) }))
|
||||
}
|
||||
|
||||
function joinGroup(code){
|
||||
function joinGroup(code) {
|
||||
var gID = code.substring(0, 8);
|
||||
var key = code.substring(8);
|
||||
if(joinedGroup.value.includes(gID)){
|
||||
if (joinedGroup.value.includes(gID)) {
|
||||
return
|
||||
}
|
||||
gID2key[gID] = {}
|
||||
@@ -234,18 +256,42 @@ function joinGroup(code){
|
||||
joinGroup_Verify(gID)
|
||||
}
|
||||
|
||||
function joinGroup_Verify(gID){
|
||||
socket.send(JSON.stringify({type:'join',type1:'verify',gID}))
|
||||
function joinGroup_Verify(gID) {
|
||||
socket.send(JSON.stringify({ type: 'join', type1: 'verify', gID }))
|
||||
}
|
||||
|
||||
function sendMsg(gID,msg){
|
||||
function sendMsg(gID, msg) {
|
||||
let key = gID2key[gID].key
|
||||
msg = encryptString(key,msg,setting)
|
||||
socket.send(JSON.stringify({type:'message',msg,gID}))
|
||||
msg = encryptString(key, msg, setting)
|
||||
socket.send(JSON.stringify({ type: 'message', msg, gID }))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 通过地址传参并加入传入的群组
|
||||
// function getQueryString(key) {
|
||||
// var reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i');
|
||||
// var r = window.location.search.substring(1).match(reg);
|
||||
// if (r != null) {
|
||||
// return unescape(r[2]);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
onMounted(()=>{
|
||||
let aP = getQueryString('aP')
|
||||
let cG = getQueryString('cG')
|
||||
if(aP === 'chat' && cG){
|
||||
groupCode.value = cG
|
||||
var st = setInterval(()=>{
|
||||
if(joinedGroup.value.includes(cG.substring(0,8))){
|
||||
clearInterval(st)
|
||||
return
|
||||
}
|
||||
joinGroup(cG)
|
||||
},2000)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
@@ -28,7 +28,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted,computed } from "vue";
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import {decrypt, decryptString, downloadFromUint8Array} from '../func/download'
|
||||
import {NInput,NFlex,NDataTable,NButton} from 'naive-ui'
|
||||
@@ -158,7 +158,11 @@ const downloadFile = code => {
|
||||
fileName: id
|
||||
};
|
||||
const options = {
|
||||
method: "GET"
|
||||
method: "GET",
|
||||
headers: {
|
||||
'Authorization': `Bearer ${setting.token}`
|
||||
// 'Content-Type':'application/json'
|
||||
},
|
||||
};
|
||||
fetch(baseUrl + "file/download?" + new URLSearchParams(params), options)
|
||||
.then(res => {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<n-card style="box-shadow: 10px 10px 5px #888888;margin-top: 100px; padding-left: 25px;padding-right: 25px; padding-bottom: 30px" size="small">
|
||||
<n-flex vertical justify="center">
|
||||
<n-flex justify="center">
|
||||
<h1 style="letter-spacing:25px;font-size: 40px;">登录</h1>
|
||||
</n-flex>
|
||||
<n-input-group>
|
||||
<n-input-group-label>账号</n-input-group-label>
|
||||
<n-input placeholder="userID" v-model:value="userIDInput" :loading="isLogining" :disabled="isLogining"></n-input>
|
||||
</n-input-group>
|
||||
<n-input-group>
|
||||
<n-input-group-label>密码</n-input-group-label>
|
||||
<n-input placeholder="userPassword" v-model:value="userPasswordInput" type="password"
|
||||
show-password-on="click" :loading="isLogining" :disabled="isLogining"></n-input>
|
||||
</n-input-group>
|
||||
<n-flex justify="end">
|
||||
<n-flex style="flex:1;align-items: center;">
|
||||
<n-checkbox size="large" label="保持登录状态" v-model:checked="keepTokenLocal" />
|
||||
</n-flex>
|
||||
<n-flex justify="end">
|
||||
<n-button @click="useGuestAccount">使用访客账号登录</n-button>
|
||||
<n-button @click="login">登录</n-button>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { NCheckbox, NInputGroupLabel, NInputGroup, NP, NFlex, NDataTable, NButton, NInput, NCard } from 'naive-ui'
|
||||
import { useSettingStore } from "@/pinia/store";
|
||||
import { useMessage, useNotification } from "naive-ui";
|
||||
import sha256 from "crypto-js/sha256.js";
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const settingData = useSettingStore();
|
||||
|
||||
const serverPort = settingData.serverPort;
|
||||
|
||||
|
||||
const userIDInput = ref('')
|
||||
const userPasswordInput = ref('')
|
||||
const isLogining = ref(false)
|
||||
const baseUrl =
|
||||
location.protocol + "//" + location.hostname + ":" + serverPort + "/";
|
||||
|
||||
const Token = computed(() => settingData.token)
|
||||
const keepTokenLocal = ref(true)
|
||||
|
||||
|
||||
|
||||
function useGuestAccount() {
|
||||
userIDInput.value = 'guest'
|
||||
userPasswordInput.value = 'guest'
|
||||
login()
|
||||
}
|
||||
async function login() {
|
||||
try {
|
||||
isLogining.value = true
|
||||
const response = await fetch(`${baseUrl}user/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
// 'Authorization': `Bearer ${Token}`
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: userIDInput.value,
|
||||
pwd: sha256(userPasswordInput.value).toString()
|
||||
})
|
||||
})
|
||||
let data = await response.json()
|
||||
if (data.status === 'Error') {
|
||||
console.log('error')
|
||||
throw ('failed')
|
||||
}
|
||||
console.log(data.token, data)
|
||||
settingData.setJWT(data.token,keepTokenLocal)
|
||||
settingData.setUserID(userIDInput.value,keepTokenLocal)
|
||||
settingData.setUserGroup(data.userGroup,keepTokenLocal)
|
||||
} catch (e) {
|
||||
isLogining.value = false
|
||||
message.error(`登录失败`);
|
||||
console.log('failed')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -37,7 +37,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted,computed } from "vue";
|
||||
// import aesjs from "aes-js";
|
||||
// import { Base64 } from "js-base64";
|
||||
import { useMessage, useNotification } from "naive-ui";
|
||||
@@ -204,6 +204,8 @@ const customRequest = ({
|
||||
"POST",
|
||||
baseUrl + "file/upload?" + new URLSearchParams(params)
|
||||
);
|
||||
console.log(setting.token)
|
||||
xhr.setRequestHeader('Authorization', `Bearer ${setting.token}`);
|
||||
xhr.send(data);
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
|
||||
Reference in new issue
Block a user