feat: added file-to-base64

This commit is contained in:
Corentin Thomasset 2021-03-14 21:20:35 +01:00
parent 5fa81533d9
commit 1034296359
No known key found for this signature in database
GPG key ID: DBD997E935996158
5 changed files with 316 additions and 47 deletions

148
components/FileUploader.vue Normal file
View file

@ -0,0 +1,148 @@
<template>
<div
class="drop-area pa-4 text-center"
:class="{'drag-over':dragging, 'pb-0':!loading}"
@dragover.prevent
@drop.prevent="imageDropped"
@dragenter="dragEnter()"
@dragend="dragEnd()"
@dragleave="dragLeave()"
@dragexit="dragExit()"
>
<div v-if="loading">
<v-progress-circular
indeterminate
color="primary"
/>
</div>
<div v-else>
<p>Drag & drop a file here</p>
<p class="or">
or
</p>
<v-btn depressed @click="manualUploadClicked">
select a file
</v-btn>
<input ref="uploadInput" type="file" hidden @change="(e) => handleFiles(e.target.files[0])">
<div v-if="allowUrl">
<p class="or">
or
</p>
<v-text-field
ref="urlInput"
v-model="url"
append-icon="fa-arrow-right"
dense
label="Paste the file url"
outlined
:error-messages="urlErrors"
@click:append="urlFilled(url)"
@keypress.enter="urlFilled(url)"
/>
</div>
</div>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'nuxt-property-decorator'
@Component
export default class FileUploader extends Vue {
@Prop({default: true}) readonly allowUrl!: boolean
dragging = false
urlErrors = null
dragEnterCounter = 0
url = ''
loading = false
imageDropped(e) {
this.dragging = false
if (e.dataTransfer.items.length > 0) {
const item = e.dataTransfer.items[0]
switch (item.kind) {
case 'string':
item.getAsString(url => this.urlFilled(url))
break
case 'file':
this.handleFiles(item.getAsFile())
break
}
}
}
dragEnter() {
this.dragEnterCounter++
this.dragging = true
}
dragLeave() {
if (--this.dragEnterCounter <= 0) {
this.dragging = false
}
}
async urlFilled(url) {
if (url && url.length > 0) {
this.loading = true
try {
const {data, headers} = await axios.get(url)
const name = url.split('/').pop()
const file = new File([data], name, {type: headers['content-type']})
this.handleFiles(file)
} catch (ignored) {
this.urlErrors = 'Incorrect url'
}
this.loading = false
}
}
dragEnd() {
this.dragging = false
}
dragExit() {
this.dragging = false
}
handleFiles(file) {
if (!file) {
return
}
this.$emit('input', file)
}
manualUploadClicked() {
this.$refs.uploadInput.click()
}
}
</script>
<style lang="less">
.drop-area {
border: 2px dashed rgba(255,255,255,0.3);
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.03);
& > *, .v-btn {
margin: 0 !important;
}
.or {
opacity: 0.7;
margin: 5px 0 !important;
}
&.drag-over {
border-color: var(--v-primary-base);
}
.v-input__icon {
button {
margin-top: 0 !important;
}
}
}
</style>

View file

@ -4,16 +4,22 @@
<v-col cols="12" xl="6" lg="8" md="10"> <v-col cols="12" xl="6" lg="8" md="10">
<div class="tool-wrapper-info"> <div class="tool-wrapper-info">
<h1>{{ config.title }}</h1> <h1>{{ config.title }}</h1>
<div class="spacer" /> <div class="spacer"/>
<div class="description"> <div class="description">
{{ config.description }} {{ config.description }}
</div> </div>
</div> </div>
<v-card flat> <template v-if="!noCard">
<v-card-text class="pa-10"> <v-card flat>
<slot /> <v-card-text class="pa-10">
</v-card-text> <slot/>
</v-card> </v-card-text>
</v-card>
</template>
<template v-else>
<slot/>
</template>
</v-col> </v-col>
</v-row> </v-row>
</div> </div>
@ -25,7 +31,8 @@ import {ToolConfig} from '~/types/ToolConfig'
@Component @Component
export default class ToolWrapper extends Vue { export default class ToolWrapper extends Vue {
@Prop() config!: ToolConfig; @Prop() readonly config!: ToolConfig;
@Prop({default: () => false}) readonly noCard!: boolean;
} }
</script> </script>
@ -34,7 +41,7 @@ export default class ToolWrapper extends Vue {
.tool-wrapper { .tool-wrapper {
height: 100%; height: 100%;
.tool-wrapper-info{ .tool-wrapper-info {
padding: 50px 0 30px; padding: 50px 0 30px;
} }
@ -49,7 +56,7 @@ export default class ToolWrapper extends Vue {
padding: 0; padding: 0;
} }
.spacer{ .spacer {
width: 200px; width: 200px;
height: 2px; height: 2px;
background: var(--v-primary-base); background: var(--v-primary-base);

View file

@ -1,29 +1,38 @@
<template> <template>
<v-row justify="center" align="center"> <v-row justify="center" align="center">
<v-col cols="12" sm="12" md="8"> <v-col cols="12" sm="12" md="12">
<h1>Yolo</h1> <h1>Yolo</h1>
<v-card v-for="(items, section) in toolRoutesSections" :key="section"> <v-row justify="center" >
<v-card-title>{{ section }}</v-card-title> <v-col
<v-card-text> cols="12"
<v-list> sm="12"
<v-list-item md="6"
v-for="(item, i) in items" v-for="(items, section) in toolRoutesSections" :key="section"
:key="i" >
:to="item.path" <v-card>
router <v-card-title>{{ section }}</v-card-title>
exact <v-card-text>
> <v-list>
<v-list-item-action> <v-list-item
<v-icon>{{ item.config.icon }}</v-icon> v-for="(item, i) in items"
</v-list-item-action> :key="i"
<v-list-item-content> :to="item.path"
<v-list-item-title v-text="item.config.title" /> router
</v-list-item-content> exact
</v-list-item> >
</v-list> <v-list-item-action>
</v-card-text> <v-icon>{{ item.config.icon }}</v-icon>
</v-card> </v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.config.title"/>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-col> </v-col>
</v-row> </v-row>
</template> </template>
@ -37,22 +46,22 @@ export default class Index extends mixins(ToolRoutesMixin) {
} }
</script> </script>
<style scopedlang="less"> <style scoped lang="less">
.v-list{ .v-list {
background: transparent !important; background: transparent !important;
} }
.v-card__title{ .v-card__title {
background: var(--v-primary-base) !important; background: var(--v-primary-base) !important;
background: linear-gradient(90deg, rgba(37,99,108,1) 0%, rgba(59,149,111,1) 60%, rgba(71,177,113,1) 100%) !important; background: linear-gradient(90deg, rgba(37, 99, 108, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(71, 177, 113, 1) 100%) !important;
padding-left: 33px; padding-left: 33px;
} }
.v-list-item{ .v-list-item {
padding-left: 31px; padding-left: 31px;
} }
.v-card__text{ .v-card__text {
padding: 0; padding: 0;
} }
</style> </style>

View file

@ -0,0 +1,105 @@
<template>
<ToolWrapper :config="config()" noCard="true">
<FileUploader v-model="file"/>
<div v-if="base64 || loading" class="mt-10">
<v-card>
<v-card-title>Result</v-card-title>
<v-card-text>
<v-textarea
v-model="base64"
label="File in base 64"
outlined
readonly
hide-details
:loading="loading"
/>
<div class="text-center mt-4">
<v-btn depressed @click="copy(base64)">
Copy base64
</v-btn>
</div>
</v-card-text>
</v-card>
</div>
</ToolWrapper>
</template>
<script lang="ts">
import {Component, Watch} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import Tool from '@/components/Tool'
import {ToolConfig} from '@/types/ToolConfig'
import FileUploader from '~/components/FileUploader'
@Component({
mixins: [CopyableMixin],
components: {FileUploader}
})
export default class FileToBase64 extends Tool {
config(): ToolConfig {
return {
title: 'File to base64',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-key-chain-variant',
keywords: ['file', 'base64']
}
}
file = null
loading = false
base64= ''
handleBase64(base64) {
this.base64 = base64
this.loading = false
}
@Watch('file')
onFile() {
this.loading = true
this.base64 = ''
const reader = new FileReader()
reader.onload = () => this.handleBase64(reader.result)
reader.onerror = () => this.handleBase64('[An error as occurred]')
reader.readAsDataURL(this.file)
}
}
// export default {
// name: 'FileToBase64',
// components: {FileUploader},
// data() {
// return {
// file: undefined,
// loading: false,
// base64: '',
// copyBase64() {
// copyToClipboard(this.base64)
// this.$toast.success('Copied to clipboard.')
// }
// }
// },
// watch: {
// file() {
// this.loading = true
// this.base64 = ''
// const reader = new FileReader()
// reader.onload = () => this.handleBase64(reader.result)
// reader.onerror = () => this.handleBase64('[An error as occurred]')
// reader.readAsDataURL(this.file)
// }
// },
// methods: {
// handleBase64(base64) {
// this.base64 = base64
// this.loading = false
// }
// }
// }
</script>
<style scoped>
</style>

View file

@ -45,7 +45,7 @@
<script lang="ts"> <script lang="ts">
import {Component} from 'nuxt-property-decorator' import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin' import {CopyableMixin} from '@/mixins/copyable.mixin'
import Tool from '@/components/Tool' import Tool from '@/components/Tool.vue'
import {ToolConfig} from '@/types/ToolConfig' import {ToolConfig} from '@/types/ToolConfig'
import CryptoJS from 'crypto-js' import CryptoJS from 'crypto-js'