add:fronted

This commit is contained in:
lan
2023-08-11 19:26:49 +08:00
parent 25f94fc0bc
commit 7b5adcc5b7
31 changed files with 3752 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
<script setup lang="ts">
</script>
<template>
<div>
<RouterView />
<div style="text-align: center; margin-top: 1rem;color: #606266">
<a style="text-decoration: none;color: #606266" target="_blank" href="https://github.com/vastsa/FileCodeBox">FileCodeBox V2.0 Beta</a>
</div>
</div>
</template>
<style scoped></style>
+86
View File
@@ -0,0 +1,86 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f5f5f5;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #212121;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
}
html{
background: var(--vt-c-white-soft);
}
html.dark{
background: var(--vt-c-black-soft);
}
body {
color: var(--color-text);
display: flex;
justify-content: center;
align-items: center;
line-height: 1.6;
font-family: Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

+55
View File
@@ -0,0 +1,55 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
min-height: 100vh;
}
.card {
max-width: 400px;
height: 100%;
background-color: #F8FBFE;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
border-radius: 20px !important;
position: relative;
}
.tools {
display: flex;
z-index: 999;
align-items: center;
}
.box {
display: inline-block;
align-items: center;
width: 1.5rem;
height: 1.5rem;
user-select: none;
cursor: pointer;
padding: 1px;
border-radius: 50%;
}
.circle {
padding: 0 4px;
}
.red {
padding: 3px;
background-color: #ff605c;
}
.yellow {
padding: 3px;
background-color: #ffbd44;
}
.green {
background-color: #00ca4e;
padding: 3px;
}
+39
View File
@@ -0,0 +1,39 @@
<script setup lang="ts">
import { useDark, useToggle } from '@vueuse/core'
import { useRoute, useRouter } from "vue-router";
const router = useRouter()
const route = useRoute()
const isDark = useDark()
const toggleDark = useToggle(isDark)
import { Moon, Sunny, Upload, Back, TakeawayBox } from "@element-plus/icons-vue";
import FileBox from "@/components/FileBox.vue";
import { useFileBoxStore } from "@/stores/fileBox";
const fileBoxStore = useFileBoxStore();
</script>
<template>
<div class="tools">
<div class="circle">
<el-icon size="17" color="#212121" class="red box" @click="router.push({'name':route.name=='home'?'send':'home'})">
<Back v-if="route.name=='send'"></Back>
<Upload v-else></Upload>
</el-icon>
</div>
<div class="circle">
<el-icon size="17" color="#212121" class="yellow box" @click="toggleDark(!isDark)">
<Moon v-if="isDark"></Moon>
<Sunny v-else></Sunny>
</el-icon>
</div>
<div class="circle">
<el-icon size="17" color="#212121" class="green box" @click="fileBoxStore.showFileBox=true">
<TakeawayBox></TakeawayBox>
</el-icon>
</div>
<file-box></file-box>
</div>
</template>
<style scoped lang="scss">
</style>
+65
View File
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { useFileDataStore } from "@/stores/fileData";
import { useFileBoxStore } from "@/stores/fileBox";
const fileStore = useFileDataStore();
const fileBoxStore = useFileBoxStore();
import QrcodeVue from "qrcode.vue";
import { useRoute } from "vue-router";
const copyText = (text: any, style = 0) => {
if (style === 1) {
text = window.location.origin + '/#/?code=' + text;
}
const temp: any = document.createElement('textarea');
temp.value = text;
document.body.appendChild(temp);
temp.select();
if (document.execCommand('copy')) {
document.execCommand('copy');
}
document.body.removeChild(temp);
};
const route = useRoute();
</script>
<template>
<el-drawer append-to-body v-model="fileBoxStore.showFileBox" direction="btt" style="max-width: 1080px;margin: auto;"
size="400">
<template #header>
<h4>文件箱</h4>
</template>
<template #default>
<div v-if="route.name=='home'">
<el-card v-for="i in fileStore.receiveData" :key="i">
{{ i }}
<template #header>
<el-button type="danger" @click="fileStore.deleteReceiveData(i)">删除</el-button>
</template>
</el-card>
</div>
<div v-else style="display: flex;flex-wrap: wrap;justify-content: center">
<el-card v-for="(value,index) in fileStore.shareData" :key="index" style="margin: 0.5rem">
<template #header>
<div style="display: flex;justify-content: space-between">
<h4 style="width: 6rem;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{{ value.name }}</h4>
<el-button size="small" type="danger" @click="fileStore.deleteShareData(index)">删除</el-button>
</div>
</template>
<div style="width: 200px;">
<el-progress v-if="value.status!='success'" striped :percentage="value.percentage" :text-inside="true"
:stroke-width="20"></el-progress>
<div style="display: flex;justify-content: space-between">
<qrcode-vue :value="value.text" :size="100"></qrcode-vue>
<div style="display: flex;flex-direction: column;justify-content: space-around">
<el-tag size="large" style="cursor: pointer" @click="copyText(value.code)">{{ value.code }}</el-tag>
<el-tag size="large" type="success" style="cursor: pointer" @click="copyText(value.code,1);">复制链接
</el-tag>
</div>
</div>
</div>
</el-card>
</div>
</template>
</el-drawer>
</template>
+157
View File
@@ -0,0 +1,157 @@
<script setup lang="ts">
import { UploadFilled } from '@element-plus/icons-vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { request } from "@/utils/request";
import { useFileDataStore } from "@/stores/fileData";
import { useFileBoxStore } from "@/stores/fileBox";
const fileBoxStore = useFileBoxStore();
const fileStore = useFileDataStore();
const props = defineProps({
shareData: {
type: Object,
default: () => {
return {
expireValue: 1,
expireStyle: 'day',
targetType: 'file',
}
}
}
})
const fileList: any = ref([])
const uploadBox: any = ref(null)
const handleOnChangeFileList = (file: any) => {
fileStore.addShareData({
'name': file.name,
'text': '',
'status': file.status,
'percentage': 0,
'size': file.size,
'type': file.raw.type,
'uid': file.uid,
});
};
const handleHttpRequest = (options: any) => {
fileBoxStore.showFileBox = true;
const formData = new FormData();
formData.append('file', options.file);
formData.append('expireValue', props.shareData.expireValue);
formData.append('expireStyle', props.shareData.expireStyle);
formData.append('targetType', props.shareData.targetType);
request(
{
url: "share/file/",
method: "post",
data: formData,
onUploadProgress: (event: any) => {
const percentage = Math.round((event.loaded * 100) / event.total) || 0;
fileStore.shareData.forEach((file: any) => {
if (file.uid === options.file.uid) {
file.percentage = percentage;
fileStore.save();
}
});
}
}
).then((res: any) => {
const data = res.data.data;
fileStore.shareData.forEach((file: any) => {
if (file.uid === options.file.uid) {
file.status = 'success';
file.text = data.text;
file.code = data.code;
fileStore.save();
}
});
}).catch((err: any) => {
fileStore.shareData.forEach((file: any) => {
if (file.uid === options.file.uid) {
file.status = 'fail';
file.text = err.message;
fileStore.save();
}
});
});
};
function pasteLister(event: any) {
const items = event.clipboardData && event.clipboardData.items;
if (items && items.length) {
for (let i = 0; i < items.length; i++) {
if (items[i].kind === 'string') {
if (items[i].type.match(/^text\/plain/)) {
items[i].getAsString(function(str) {
console.log(str);
});
}
} else {
const file: any = items[i].getAsFile();
if (file) {
console.log(file);
const uid = Date.now();
file.uid = uid;
fileStore.addShareData({
'name': file.name,
'text': '',
'status': 'ready',
'percentage': 0,
'size': file.size,
'type': file.type,
'uid': uid,
});
handleHttpRequest({
file: file,
})
}
}
}
}
}
onUnmounted(()=>{
// 清除剪切板事件
document.removeEventListener('paste', pasteLister);
})
onMounted(()=>{
document.addEventListener('paste', pasteLister);
})
</script>
<template>
<div>
<el-upload
class="upload-demo"
drag
action="http://127.0.0.1:8000/share"
multiple
:show-file-list="false"
ref="uploadBox"
v-model:file-list="fileList"
:on-change="handleOnChangeFileList"
:http-request="handleHttpRequest"
>
<el-icon class="el-icon--upload">
<upload-filled/>
</el-icon>
<div class="el-upload__text">
将文字文件拖粘贴到此处 <em>点击上传</em>
</div>
<div class="el-upload__text" style="font-size: 10px;">天数&lt;7或限制次数24h后删除</div>
<template #tip>
<div class="el-upload__tip">
</div>
</template>
</el-upload>
</div>
</template>
<style lang="scss">
.el-upload {
border-radius: 20px;
}
.el-upload-dragger {
box-shadow: 3px 3px 0 0 rgba(0, 0, 0, 0.2);
border-radius: 20px;
}
</style>
+64
View File
@@ -0,0 +1,64 @@
<script setup lang="ts">
import { ref } from 'vue'
import { request } from "@/utils/request";
const shareText = ref('')
import { useFileDataStore } from "@/stores/fileData";
import { useFileBoxStore } from "@/stores/fileBox";
const fileBoxStore = useFileBoxStore();
const fileStore = useFileDataStore();
const props = defineProps({
shareData: {
type: Object,
default: () => {
return {
expireValue: 1,
expireStyle: 'day',
targetType: 'text',
}
}
}
})
const handleSubmitShareText = ()=>{
const formData = new FormData();
formData.append('text', shareText.value);
formData.append('expireValue', props.shareData.expireValue);
formData.append('expireStyle', props.shareData.expireStyle);
formData.append('targetType', props.shareData.targetType);
request({
'url': 'share/text/',
'method': 'post',
'data': formData,
}).then((res: any) => {
const data = res.data.data;
fileBoxStore.showFileBox = true;
fileStore.addShareData({
'name': '文本分享',
'text': data.text,
'code': data.code,
'status': 'success',
'percentage': 100,
'size': shareText.value.length,
'type': 'text',
'uid': Date.now(),
})
})
}
</script>
<template>
<div style="position: relative">
<el-input
placeholder="请输入您要寄出的文本"
v-model="shareText"
type="textarea"
:rows="9"
:input-style="{'border-radius':'20px','border':'1px dashed var(--el-border-color)','box-shadow':'none'}"
>
</el-input>
<el-button @click="handleSubmitShareText" style="position: absolute;right: 0;top: 0;border-radius: 0 20px 0 20px;margin: 1px;background: rgba(255,255,255,0.2)" size="large">发送</el-button>
</div>
</template>
<style scoped lang="scss">
</style>
+13
View File
@@ -0,0 +1,13 @@
import './assets/main.css'
import 'element-plus/theme-chalk/dark/css-vars.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
+19
View File
@@ -0,0 +1,19 @@
import { createRouter, createWebHashHistory } from 'vue-router';
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('@/views/HomeView.vue'),
},
{
path: '/send',
name: 'send',
component: () => import('@/views/SendView.vue'),
},
],
});
export default router;
+7
View File
@@ -0,0 +1,7 @@
import { defineStore } from 'pinia';
import {ref} from "vue";
export const useFileBoxStore = defineStore('fileBox', () => {
const showFileBox = ref(false);
return { showFileBox };
});
+31
View File
@@ -0,0 +1,31 @@
import { defineStore } from 'pinia';
import {reactive} from "vue";
export const useFileDataStore = defineStore('fileData', () => {
const receiveData = reactive(JSON.parse(localStorage.getItem('receiveData')||'') || []); // 接收的数据
const shareData = reactive(JSON.parse(localStorage.getItem('shareData')||'') || []); // 接收的数据
function save() {
localStorage.setItem('receiveData', JSON.stringify(receiveData));
localStorage.setItem('shareData', JSON.stringify(shareData));
}
function addReceiveData(data:any) {
receiveData.push(data);
save();
}
function addShareData(data:any) {
shareData.push(data);
save();
}
function deleteReceiveData(index: number) {
receiveData.splice(index, 1);
save();
}
function deleteShareData(index: number) {
shareData.splice(index, 1);
save();
}
return { receiveData, shareData, save, addShareData, addReceiveData, deleteReceiveData, deleteShareData };
});
+18
View File
@@ -0,0 +1,18 @@
// @ts-ignore
import axios from "axios";
const instance = axios.create({
baseURL: "",
timeout: 6000000,
headers:{
'Authorization':localStorage.getItem('auth')
}
});
const Request = axios.create({
baseURL: "http://localhost:8000",
timeout: 6000000,
});
export const request = Request;
export const http = instance;
+60
View File
@@ -0,0 +1,60 @@
<script setup lang="ts">
import { Upload, TakeawayBox } from '@element-plus/icons-vue';
import { ref } from 'vue'
import { useRouter } from "vue-router";
import CardTools from "@/components/CardTools.vue";
import { useFileBoxStore } from "@/stores/fileBox";
const fileBoxStore = useFileBoxStore();
const router = useRouter()
const code = ref('')
const listenInput = (num: number) => {
console.log('listenInput',num)
};
</script>
<template>
<main>
<el-card class="card" style="padding-bottom: 1rem">
<CardTools/>
<el-row style="text-align: center">
<el-col :span="24">
<el-input v-model="code" class="code-input" round autofocus clearable maxlength="5" placeholder="请输入五位取件码"/>
</el-col>
<el-col :span=8 v-for="i in 9" :key="i">
<el-button class="key-button" round @click="listenInput(i)">{{ i }}</el-button>
</el-col>
<el-col :span=8>
<el-button @click="router.push({'name':'send'})" class="key-button" :icon="Upload" round>
</el-button>
</el-col>
<el-col :span=8>
<el-button class="key-button" round @click="listenInput(0)">0</el-button>
</el-col>
<el-col :span=8>
<el-button class="key-button" round :icon="TakeawayBox" @click="fileBoxStore.showFileBox=true">
</el-button>
</el-col>
</el-row>
</el-card>
</main>
</template>
<style lang='scss'>
.key-button{
width: 6rem;
height: 6rem;
margin: 0.2rem;
font-size: 2rem;
font-weight: bold;
text-align: center;
}
.code-input {
height: 100px;
font-size: 30px;
font-weight: bold;
margin: 1rem 0;
.el-input__wrapper{
border-radius: 20px !important;
}
}
</style>
+53
View File
@@ -0,0 +1,53 @@
<script setup lang="ts">
import { ref } from 'vue'
import CardTools from "@/components/CardTools.vue";
import UploadFile from "@/components/UploadFile.vue";
import UploadText from "@/components/UploadText.vue";
const shareData = ref({
expireValue: 1,
expireStyle: 'day',
targetType: 'file',
})
</script>
<template>
<main>
<el-card class="card" style="padding: 1rem;position: relative" :body-style="{ padding: '0px 0px 20px 0px' }">
<card-tools/>
<div style="display: flex;margin-top: 1rem">
<div>
<el-input
v-model="shareData.expireValue"
style="width: 200px"
placeholder="请输入值"
>
<template #prepend>
<el-select v-model="shareData.expireStyle" placeholder="过期方式" style="width: 75px">
<el-option label="天数" value="day" />
<el-option label="小时" value="hour" />
<el-option label="分钟" value="minute" />
<el-option label="永久" value="forever" />
<el-option label="次数" value="count" />
</el-select>
</template>
<template #append>
<span v-if="shareData.expireStyle=='day'"></span>
<span v-else-if="shareData.expireStyle=='hour'"></span>
<span v-else-if="shareData.expireStyle=='minute'"></span>
<span v-else-if="shareData.expireStyle=='forever'">👌</span>
<span v-else-if="shareData.expireStyle=='count'"></span>
</template>
</el-input>
</div>
<el-radio-group v-model="shareData.targetType" style="margin-left: 1rem;">
<el-radio label="file">文件</el-radio>
<el-radio label="text">文本</el-radio>
</el-radio-group>
</div>
<div style="margin-top: 1rem">
<upload-file :shareData="shareData" v-if="shareData.targetType=='file'"/>
<upload-text :shareData="shareData" v-else-if="shareData.targetType=='text'"/>
</div>
</el-card>
</main>
</template>