add:fronted
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
'extends': [
|
||||||
|
'plugin:vue/vue3-essential',
|
||||||
|
'eslint:recommended',
|
||||||
|
'@vue/eslint-config-typescript',
|
||||||
|
'@vue/eslint-config-prettier/skip-formatting'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
.git
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
|
"semi": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "none"
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||||
|
}
|
||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
CardTools: typeof import('./src/components/CardTools.vue')['default']
|
||||||
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
|
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
|
ElOption: typeof import('element-plus/es')['ElOption']
|
||||||
|
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||||
|
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||||
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
|
ElRow: typeof import('element-plus/es')['ElRow']
|
||||||
|
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||||
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||||
|
ElTag: typeof import('element-plus/es')['ElTag']
|
||||||
|
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||||
|
FileBox: typeof import('./src/components/FileBox.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
UploadFile: typeof import('./src/components/UploadFile.vue')['default']
|
||||||
|
UploadText: typeof import('./src/components/UploadText.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/logo_small.png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>FileCodeBox</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "fcb-fronted",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "run-p type-check build-only",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"build-only": "vite build",
|
||||||
|
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||||
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||||
|
"format": "prettier --write src/"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.4.0",
|
||||||
|
"element-plus": "^2.3.9",
|
||||||
|
"pinia": "^2.1.4",
|
||||||
|
"qrcode.vue": "^3.4.1",
|
||||||
|
"sass": "^1.64.2",
|
||||||
|
"unplugin-auto-import": "^0.16.6",
|
||||||
|
"unplugin-vue-components": "^0.25.1",
|
||||||
|
"vue": "^3.3.4",
|
||||||
|
"vue-router": "^4.2.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rushstack/eslint-patch": "^1.3.2",
|
||||||
|
"@tsconfig/node18": "^18.2.0",
|
||||||
|
"@types/node": "^18.17.0",
|
||||||
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
|
"@vue/eslint-config-prettier": "^8.0.0",
|
||||||
|
"@vue/eslint-config-typescript": "^11.0.3",
|
||||||
|
"@vue/tsconfig": "^0.4.0",
|
||||||
|
"eslint": "^8.45.0",
|
||||||
|
"eslint-plugin-vue": "^9.15.1",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
|
"typescript": "~5.1.6",
|
||||||
|
"vite": "^4.4.6",
|
||||||
|
"vue-tsc": "^1.8.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -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>
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 |
@@ -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;
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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;">天数<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>
|
||||||
@@ -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>
|
||||||
@@ -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')
|
||||||
@@ -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;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import {ref} from "vue";
|
||||||
|
|
||||||
|
export const useFileBoxStore = defineStore('fileBox', () => {
|
||||||
|
const showFileBox = ref(false);
|
||||||
|
return { showFileBox };
|
||||||
|
});
|
||||||
@@ -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 };
|
||||||
|
});
|
||||||
@@ -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;
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||||
|
"exclude": ["src/**/__tests__/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node18/tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"vite.config.*",
|
||||||
|
"vitest.config.*",
|
||||||
|
"cypress.config.*",
|
||||||
|
"nightwatch.conf.*",
|
||||||
|
"playwright.config.*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"types": ["node"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
AutoImport({
|
||||||
|
resolvers: [ElementPlusResolver()],
|
||||||
|
}),
|
||||||
|
Components({
|
||||||
|
resolvers: [ElementPlusResolver()],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
|||||||
|
# @Time : 2023/8/9 23:23
|
||||||
|
# @Author : Lan
|
||||||
|
# @File : main.py
|
||||||
|
# @Software: PyCharm
|
||||||
|
import random
|
||||||
|
|
||||||
|
from fastapi import FastAPI, UploadFile, File, Form
|
||||||
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/share/text/')
|
||||||
|
async def share_text(text: str = Form(...), expireValue: int = Form(...), expireStyle: str = Form(...), targetType: str = Form(...)):
|
||||||
|
return {
|
||||||
|
'code': 200,
|
||||||
|
'msg': 'success',
|
||||||
|
'data': {
|
||||||
|
'code': random.randint(100000, 999999),
|
||||||
|
'text': text,
|
||||||
|
'name': '文本分享',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/share/file/')
|
||||||
|
async def share_file(file: UploadFile = File(default=None)):
|
||||||
|
return {
|
||||||
|
'code': 200,
|
||||||
|
'msg': 'success',
|
||||||
|
'data': {
|
||||||
|
'code': random.randint(100000, 999999),
|
||||||
|
'text': '/share/{}'.format(random.randint(100000, 999999)),
|
||||||
|
'name': file.filename,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user