mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-22 07:46:15 -04:00
feat: added file-to-base64
This commit is contained in:
parent
5fa81533d9
commit
1034296359
5 changed files with 316 additions and 47 deletions
148
components/FileUploader.vue
Normal file
148
components/FileUploader.vue
Normal 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>
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
105
pages/tools/Web/file-to-base64.vue
Normal file
105
pages/tools/Web/file-to-base64.vue
Normal 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>
|
|
@ -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'
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue