Files
miaoShare/src/components/chatui.vue
T

262 lines
11 KiB
Vue

<template>
<!-- <div style="border: 1px solid rgb(239, 239, 245);border-radius: 5px;position: relative;"> -->
<n-scrollbar
style="padding: 10px;padding-right: 15px; border: 1px solid rgb(239, 239, 245);border-radius: 5px;position: relative;">
<n-image-group>
<div v-for="msg of props.messages">
<div style="color:slategrey;">{{ `${msg.from.split('-')[0]} ${msg.timestampString}` }}</div>
<div style="background-color: rgb(239, 239, 245); padding: 2px 7px; width: fit-content; border-radius: 5px; white-space: pre-wrap;"
v-if="msg.type1 === 'text'">
<span>{{ msg.msg }}</span>
<!-- <span v-if="msg.type1 === 'img'"> -->
<!-- <n-image :src="imgUrlCache[msg.msg]" width="100" style="margin-top: 5px;border-radius: 5px;" /> -->
<!-- img->>{{ msg.msg }} -->
<!-- </span> -->
</div>
<div v-if="msg.type1 === 'img'">
<span>
<n-image :src="imgUrlCache[msg.msg]" width="100" style="margin-top: 5px;border-radius: 5px;" />
<n-skeleton v-show="!imgUrlCache[msg.msg]" height="80px" width="100px" :sharp="false"
style="border-radius: 5px;" />
</span>
</div>
<div v-if="msg.type1 === 'file'"
style="background-color: rgb(239, 239, 245); padding: 2px 7px; width: fit-content; border-radius: 5px;">
<n-flex style="justify-content: center;align-items: center;user-select: none;cursor: pointer;"
@click="downloadFile(msg.msg)">
<n-icon :component="DownloadIcon" size="50" style="margin-top: 0px;"></n-icon>
<n-flex vertical v-if="fileInfoCache[msg.msg]">
<div style="font-weight: 500;margin-top: 8px;font-size: large;">
{{ fileInfoCache[msg.msg].fileName ? fileInfoCache[msg.msg].fileName : 'Unknow' }}
</div>
<n-flex style="margin-bottom: 8px;" justify="space-between">
<div>
{{ fileInfoCache[msg.msg].fileSize ? fileInfoCache[msg.msg].fileSize : 'Unknow'
}}
</div>
<!-- lets show u a bullshit -->
<div>
{{ fileStatus2String[fileInfoCache[msg.msg].status ? fileInfoCache[msg.msg].status :
'Unknow'] ? fileStatus2String[fileInfoCache[msg.msg].status ?
fileInfoCache[msg.msg].status : 'Unknow'] : fileInfoCache[msg.msg].status ?
fileInfoCache[msg.msg].status : 'Unknow' }}
</div>
</n-flex>
</n-flex>
</n-flex>
<!-- {{ fileInfoCache[msg.msg] }} -->
</div>
</div>
<n-empty description="无可显示消息" v-show="props.messages.length === 0" style="margin-top: 25vh;">
<template #extra>
</template>
</n-empty>
<div style="height: 30px;" v-show="props.messages.length !== 0"></div>
</n-image-group>
</n-scrollbar>
<!-- </div> -->
</template>
<script setup>
import { NScrollbar, NEmpty, NImage, NImageGroup, NSkeleton, NIcon, NFlex } from 'naive-ui'
import { ref, watch } from 'vue';
import { useSettingStore } from "@/pinia/store";
import { decrypt, decryptString,downloadFromUint8Array } from '../func/download'
import { CubeOutline as DownloadIcon } from '@vicons/ionicons5'
import fileSize2String from '@/func/fileSize2String'
const setting = useSettingStore();
const serverPort = setting.serverPort;
const baseUrl = location.protocol + "//" + location.hostname + ":" + serverPort + "/";
const props = defineProps({
messages: {
type: Array,
required: true
},
})
async function getFileInfo(code) {
let id = code.substring(0, 8);
const params = {
fileName: id
};
const options = {
method: "GET",
headers: {
'Authorization': `Bearer ${setting.token}`
// 'Content-Type':'application/json'
},
};
let response = await fetch(baseUrl + "file/info?" + new URLSearchParams(params), options)
let info = await response.json()
return info
}
async function downloadFile(code) {
if (fileInfoCache.value[code].status === 'waittingDownload') {
return new Promise((resolve, reject) => {
let id = code.substring(0, 8);
let key = code.substring(8);
const params = {
fileName: id
};
const options = {
method: "GET",
headers: {
'Authorization': `Bearer ${setting.token}`
},
};
fetch(baseUrl + "file/download?" + new URLSearchParams(params), options)
.then(res => {
// fileName = res.headers.get("File-Name");
// fileName = decryptString(key, fileName);
// updateDownloadQuene(code, "fileName", fileName);
const total = +res.headers.get("Content-Length");
let loaded = 0;
const clone = res.clone();
const reader = res.body.getReader();
// 感觉怪怪的
const read = async () => {
const { done, value } = await reader.read();
if (done) {
// console.log(clone)
return clone;
}
loaded += value.length;
const percent = (loaded / total) * 100;
fileInfoCache.value[code].status = `下载进度:${percent.toFixed(2)}%`;
return read();
};
return read();
})
.then(res => {
var data = res.blob();
return data;
})
.then(data => {
decrypt(key, data).then(data => {
fileInfoCache.value[code].status = `finish`;
fileInfoCache.value[code].data = data
downloadFromUint8Array(data,fileInfoCache.value[code].fileName)
resolve(data)
});
})
.catch(err => {
fileInfoCache.value[code].status = `error`;
console.error(err);
});
})
}else if(fileInfoCache.value[code].status === 'finish'){
downloadFromUint8Array(fileInfoCache.value[code].data,fileInfoCache.value[code].fileName)
}
};
async function downloadImage(code) {
return new Promise((resolve, reject) => {
var id = code.substring(0, 8);
var key = code.substring(8);
var fileName;
const params = {
fileName: id
};
const options = {
method: "GET",
headers: {
'Authorization': `Bearer ${setting.token}`
// 'Content-Type':'application/json'
},
};
fetch(baseUrl + "file/download?" + new URLSearchParams(params), options)
.then(res => {
// fileName = res.headers.get("File-Name");
// fileName = decryptString(key, fileName);
// updateDownloadQuene(code, "fileName", fileName);
const total = +res.headers.get("Content-Length");
let loaded = 0;
const clone = res.clone();
const reader = res.body.getReader();
// 感觉怪怪的
const read = async () => {
const { done, value } = await reader.read();
if (done) {
// console.log(clone)
return clone;
}
loaded += value.length;
// const percent = (loaded / total) * 100;
// downloadProcess['downloadProcess'] = `下载进度:${percent.toFixed(2)}%`;
// updateDownloadQuene(
// code,
// "downloadProcess",
// `下载进度:${percent.toFixed(2)}%`
// );
// downloadProcess.value = percent.toFixed(2);
return read();
};
return read();
})
.then(res => {
var data = res.blob();
return data;
})
.then(data => {
// downloadProcess['downloadProcess'] = '解密中'
// updateDownloadQuene(code, "downloadProcess", `解密中`);
decrypt(key, data).then(data => {
// updateDownloadQuene(code, "downloadProcess", `已完成`);
// const url = URL.createObjectURL(new Blob(data))
resolve(data)
});
})
.catch(err => {
// updateDownloadQuene(
// code,
// "fileName",
// `出错,可能是文件过期或下载次数用完`
// );
// updateDownloadQuene(code, "downloadProcess", `出错`);
console.error(err);
});
})
};
const imgUrlCache = ref({})
const fileInfoCache = ref({})
const fileStatus2String = ref({
waittingDownload: '点击下载',
downloading: '下载中',
finish: '点击获取',
Unknow: '获取文件信息中',
error: '出错'
})
const imgDownloadingQueue = []
const getFileInfoQueue = []
watch(() => props.messages, async () => {
for (let msg of props.messages) {
if (msg.type1 === 'img') {
if (!imgDownloadingQueue.includes(msg.msg)) {
imgDownloadingQueue.push(msg.msg)
const imgData = await downloadImage(msg.msg)
const url = URL.createObjectURL(new Blob([imgData]))
imgUrlCache.value[msg.msg] = url
// console.log(new Blob(imgData), imgData, url, imgUrlCache.value, imgDownloadingQueue)
}
} else if (msg.type1 === 'file') {
if (!getFileInfoQueue.includes(msg.msg)) {
getFileInfoQueue.push(msg.msg)
let key = msg.msg.substring(8);
const info = await getFileInfo(msg.msg)
info.fileName = decryptString(key, info.fileName)
info.fileSize = fileSize2String(info.fileSize)
info.status = 'waittingDownload'
fileInfoCache.value[msg.msg] = info
console.log(fileInfoCache.value)
}
}
}
})
</script>
<style></style>