added file uploads to frontend
This commit is contained in:
parent
5ba4019e36
commit
19af921913
@ -15,8 +15,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="transfer-area">
|
<div class="transfer-area">
|
||||||
<div class="transfers" v-if="transfers.length > 0">
|
<div class="transfers" v-if="transfers.length > 0">
|
||||||
<p>Downloads</p>
|
<p v-if="downloads.length > 0">Downloads</p>
|
||||||
<div v-for="download in downloads" :key="download.name" class="transfers-list-item">
|
<div v-for="download in downloads" :key="download.id" class="transfers-list-item">
|
||||||
<div class="transfer-info">
|
<div class="transfer-info">
|
||||||
<p>{{ download.name }}</p>
|
<p>{{ download.name }}</p>
|
||||||
<p class="file-size">{{ Math.min(100, Math.round(download.progress / download.size * 100))}}%</p>
|
<p class="file-size">{{ Math.min(100, Math.round(download.progress / download.size * 100))}}%</p>
|
||||||
@ -25,10 +25,22 @@
|
|||||||
<progress class="transfer-progress" :aria-label="download.name + ' progress'" :value="download.progress"
|
<progress class="transfer-progress" :aria-label="download.name + ' progress'" :value="download.progress"
|
||||||
:max="download.size"></progress>
|
:max="download.size"></progress>
|
||||||
</div>
|
</div>
|
||||||
|
<p v-if="uploads.length > 0">Uploads</p>
|
||||||
|
<div v-for="upload in uploads" :key="upload.id" class="transfers-list-item">
|
||||||
|
<div class="transfer-info">
|
||||||
|
<p>{{ upload.name }}</p>
|
||||||
|
<p class="file-size">{{ Math.min(100, Math.round(upload.progress / upload.size * 100))}}%</p>
|
||||||
|
<i class="fas fa-xmark remove-transfer" @click="() => removeTransfer(upload)"></i>
|
||||||
|
</div>
|
||||||
|
<progress class="transfer-progress" :aria-label="upload.name + ' progress'" :value="upload.progress"
|
||||||
|
:max="upload.size"></progress>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="upload-area" @dragover.prevent @drop.prevent="onFileDrop">
|
<div class="upload-area" :class="{ 'upload-area-drag': uploadAreaDrag }"
|
||||||
|
@dragover.prevent="() => uploadAreaDrag = true" @dragleave.prevent="() => uploadAreaDrag = false"
|
||||||
|
@drop.prevent="(e) => upload(e.dataTransfer)" @click="openFileBrowser">
|
||||||
<i class="fas fa-file-arrow-up" />
|
<i class="fas fa-file-arrow-up" />
|
||||||
<p>Drag files here to upload</p>
|
<p>Click or drag files here to upload</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -116,6 +128,9 @@
|
|||||||
margin: 10px 10px 10px 10px;
|
margin: 10px 10px 10px 10px;
|
||||||
background-color: rgba($color: #fff, $alpha: 0.05);
|
background-color: rgba($color: #fff, $alpha: 0.05);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transfers > p {
|
.transfers > p {
|
||||||
@ -145,6 +160,14 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload-area:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-area-drag, .upload-area:hover {
|
||||||
|
background-color: rgba($color: #fff, $alpha: 0.10);
|
||||||
|
}
|
||||||
|
|
||||||
.upload-area > i {
|
.upload-area > i {
|
||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
margin: 10px 10px 10px 10px;
|
margin: 10px 10px 10px 10px;
|
||||||
@ -163,7 +186,7 @@
|
|||||||
|
|
||||||
import Markdown from './markdown'
|
import Markdown from './markdown'
|
||||||
import Content from './context.vue'
|
import Content from './context.vue'
|
||||||
import { FileTransfer } from '~/neko/types'
|
import { FileTransfer } from '~/neko/types'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
name: 'neko-files',
|
name: 'neko-files',
|
||||||
@ -174,6 +197,8 @@ import { FileTransfer } from '~/neko/types'
|
|||||||
})
|
})
|
||||||
export default class extends Vue {
|
export default class extends Vue {
|
||||||
|
|
||||||
|
public uploadAreaDrag: boolean = false;
|
||||||
|
|
||||||
get cwd() {
|
get cwd() {
|
||||||
return this.$accessor.files.cwd
|
return this.$accessor.files.cwd
|
||||||
}
|
}
|
||||||
@ -208,8 +233,8 @@ import { FileTransfer } from '~/neko/types'
|
|||||||
id: Math.round(Math.random() * 10000),
|
id: Math.round(Math.random() * 10000),
|
||||||
name: item.name,
|
name: item.name,
|
||||||
direction: 'download',
|
direction: 'download',
|
||||||
// this is just an estimation, but for large files the content length
|
// this may be smaller than the actual transfer amount, but for large files the
|
||||||
// is not sent (chunked transfer)
|
// content length is not sent (chunked transfer)
|
||||||
size: item.size,
|
size: item.size,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
@ -228,6 +253,8 @@ import { FileTransfer } from '~/neko/types'
|
|||||||
}
|
}
|
||||||
if (transfer.progress === transfer.size) {
|
if (transfer.progress === transfer.size) {
|
||||||
transfer.status = 'completed'
|
transfer.status = 'completed'
|
||||||
|
} else if (transfer.status !== 'inprogress') {
|
||||||
|
transfer.status = 'inprogress'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
@ -245,6 +272,64 @@ import { FileTransfer } from '~/neko/types'
|
|||||||
this.$accessor.files.addTransfer(transfer)
|
this.$accessor.files.addTransfer(transfer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upload(dt: DataTransfer) {
|
||||||
|
this.uploadAreaDrag = false
|
||||||
|
|
||||||
|
for (const file of dt.files) {
|
||||||
|
const formdata = new FormData()
|
||||||
|
formdata.append("files", file, file.name)
|
||||||
|
|
||||||
|
const url = `/file?pwd=${this.$accessor.password}`
|
||||||
|
let transfer: FileTransfer = {
|
||||||
|
id: Math.round(Math.random() * 10000),
|
||||||
|
name: file.name,
|
||||||
|
direction: 'upload',
|
||||||
|
size: file.size,
|
||||||
|
progress: 0,
|
||||||
|
status: 'pending',
|
||||||
|
axios: null,
|
||||||
|
abortController: null
|
||||||
|
}
|
||||||
|
transfer.abortController = new AbortController()
|
||||||
|
this.$http.post(url, formdata, {
|
||||||
|
onUploadProgress: (x: any) => {
|
||||||
|
transfer.progress = x.loaded
|
||||||
|
|
||||||
|
if (transfer.size !== x.total) {
|
||||||
|
transfer.size = x.total
|
||||||
|
}
|
||||||
|
if (transfer.progress === transfer.size) {
|
||||||
|
transfer.status = 'completed'
|
||||||
|
} else if (transfer.status !== 'inprogress') {
|
||||||
|
transfer.status = 'inprogress'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
this.$log.error(err)
|
||||||
|
})
|
||||||
|
this.$accessor.files.addTransfer(transfer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openFileBrowser() {
|
||||||
|
const input = document.createElement('input')
|
||||||
|
input.type = 'file'
|
||||||
|
input.setAttribute('multiple', 'true')
|
||||||
|
input.click()
|
||||||
|
|
||||||
|
input.onchange = (e) => {
|
||||||
|
if (e === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const dt = new DataTransfer()
|
||||||
|
const target = e.target as any
|
||||||
|
for (const f of target.files) {
|
||||||
|
dt.items.add(f)
|
||||||
|
}
|
||||||
|
this.upload(dt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
removeTransfer(transfer: FileTransfer) {
|
removeTransfer(transfer: FileTransfer) {
|
||||||
if (transfer.status !== 'completed') {
|
if (transfer.status !== 'completed') {
|
||||||
transfer.abortController?.abort()
|
transfer.abortController?.abort()
|
||||||
@ -296,10 +381,6 @@ import { FileTransfer } from '~/neko/types'
|
|||||||
return `${(size / 1000 ** 4).toFixed(3)} tb`
|
return `${(size / 1000 ** 4).toFixed(3)} tb`
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileDrop(e: any) {
|
|
||||||
console.log('file dropped', e)
|
|
||||||
console.log(e.dataTransfer.files)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user