first commit

This commit is contained in:
Lan
2025-02-15 22:10:36 +08:00
commit 0205555d32
47 changed files with 6963 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
<template>
<main style="text-align: center">
<span>{{ t('admin.about.source1') }}
<em>
<a style="color: #333" href="https://github.com/vastsa/FileCodeBox">{{ t('admin.about.source2') }}</a>
</em>
</span>
</main>
</template>
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
</script>
+92
View File
@@ -0,0 +1,92 @@
<template>
<el-container v-if="adminData.isAdmin" style="height: 100vh;width: 100vw;position: relative;user-select: none">
<el-header>
<el-menu mode="horizontal" router :default-active="route.path">
<el-menu-item v-for="menu in menus" :index="menu.path" :key="menu.path">{{menu.name}}</el-menu-item>
<el-menu-item style="float: right" @click="toggleDark(!isDark)">{{ t('admin.menu.color') }}</el-menu-item>
<el-menu-item style="float: right" @click="logout()">{{ t('admin.menu.signout') }}</el-menu-item>
</el-menu>
</el-header>
<el-main>
<router-view/>
</el-main>
</el-container>
<el-form size="large" v-else>
<el-form-item :label="t('admin.login.managePassword')">
<el-input
v-model="adminData.adminPassword"
:placeholder="t('admin.login.passwordNotEmpty')"
type="password"
>
<template #append>
<el-button @click="refreshLoginStatus">{{ t('admin.login.login') }}</el-button>
</template>
</el-input>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { useDark, useToggle } from '@vueuse/core';
import { ref } from "vue";
const isDark = useDark()
const toggleDark = useToggle(isDark)
import { useRoute } from 'vue-router';
import { useAdminData } from "@/stores/adminData";
import { request } from "@/utils/request";
import { ElMessage } from "element-plus";
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const adminData = useAdminData();
const route = useRoute();
const menus = ref([
{
name: t('admin.menu.fileManage'),
path: '/admin',
},
{
name: t('admin.menu.systemSetting'),
path: '/admin/setting',
},
{
name: t('admin.menu.local'),
path: '/admin/local',
},
{
name: t('admin.menu.about'),
path: '/admin/about',
},
{
name: t('admin.menu.send'),
path: '/#/send',
},
{
name: t('admin.menu.receive'),
path: '/#/',
},
]);
const refreshLoginStatus = () => {
request({
url: '/admin/login',
method: 'post',
data: {
password: adminData.adminPassword,
},
}).then((res: any) => {
if (res.code === 200) {
adminData.updateAdminPwd(res.detail.token);
ElMessage.success(t('admin.login.loginSuccess'));
} else {
localStorage.clear();
ElMessage.error(t('admin.login.loginError'));
}
});
};
const logout = () => {
localStorage.clear();
};
</script>
<style lang="scss" scoped>
</style>
+147
View File
@@ -0,0 +1,147 @@
<template>
<main>
<el-table size="large" stripe :data="tableData" style="width: 100%">
<el-table-column prop="code" :label="t('admin.fileView.code')" />
<el-table-column prop="prefix" :label="t('admin.fileView.prefix')" />
<el-table-column prop="suffix" :label="t('admin.fileView.suffix')" />
<el-table-column prop="text" :label="t('admin.fileView.text')">
<template #default="scope">
<span style="width: 6rem;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{{ scope.row.text }}</span>
</template>
</el-table-column>
<el-table-column prop="used_count" :label="t('admin.fileView.used_count')" />
<el-table-column prop="expired_count" :label="t('admin.fileView.expired_count')">
<template #default="scope">
<span>{{ scope.row.expired_count > -1 ? scope.row.expired_count : t('admin.fileView.unlimited_count') }}</span>
</template>
</el-table-column>
<el-table-column prop="size" :label="t('admin.fileView.size')">
<template #default="scope">
<span>{{ Math.round(scope.row.size/1024/1024*100)/100 }}MB</span>
</template>
</el-table-column>
<el-table-column prop="expired_at" :label="t('admin.fileView.expired_at')">
<template #default="scope">
<span>{{ scope.row.expired_at ? formatTimestamp(scope.row.expired_at) : t('admin.fileView.forever') }}</span>
</template>
</el-table-column>
<el-table-column prop="file_path" :label="t('admin.fileView.file_path')" />
<el-table-column>
<template #header>
{{ t('admin.fileView.action')}}
</template>
<template #default="scope">
<el-button type="danger" size="small" @click="deleteFile(scope.row.id)">{{ t('admin.fileView.delete') }}</el-button>
<el-button type="success" size="small" @click="downloadFile(scope.row.id)" v-if="scope.row.file_path">{{ t('admin.fileView.download') }}</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination @size-change="updatePageSize" @current-change="updateCurrentPage" background layout="prev, pager, next" :page-size="params.size" :page-count="Math.round(params.total/params.size)" :total="params.total" />
</main>
</template>
<script lang="ts" setup>
import { request } from "@/utils/request";
import { formatTimestamp } from "@/utils/timestamp-format"
import { ref } from "vue";
import { ElMessage } from "element-plus";
const tableData = ref([]);
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const params = ref({
page: 1,
size: 10,
total: 0,
});
const updateCurrentPage = (currentPage: number) => {
params.value.page = currentPage;
refreshData();
};
const deleteFile = (id: number) => {
request({
url: '/admin/file/delete',
method: 'delete',
data: {
id,
},
}).then(() => {
ElMessage.success(t('admin.fileView.delete_success'));
refreshData();
});
};
const downloadFile = (id: number) => {
request({
url: '/admin/file/download',
method: 'get',
params: {
id,
},
responseType: 'blob'
}).then((response: any) => {
const contentDisposition = response.headers['content-disposition'];
let filename = 'file';
// 使用正则表达式来提取文件名
const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (filenameMatch != null && filenameMatch[1]) {
// 去除文件名周围的引号
filename = filenameMatch[1].replace(/['"]/g, '');
}
// @ts-ignore
if (window.showSaveFilePicker)
saveFileByWebApi(response.data, filename).catch(() => {
// ElMessage.error('文件保存函数不支持您的浏览器');
saveFileByElementA(response.data, filename).catch(() => {
ElMessage.error(t('admin.fileView.download_fail'));
})
})
else
saveFileByElementA(response.data, filename).catch(() => {
ElMessage.error(t('admin.fileView.download_fail'));
})
});
};
async function saveFileByElementA(fileBlob: Blob, filename: string) {
const downloadUrl = window.URL.createObjectURL(fileBlob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = filename; // 设置下载文件名
document.body.appendChild(link);
link.click();
// 清理并释放URL对象
window.URL.revokeObjectURL(downloadUrl);
document.body.removeChild(link);
}
async function saveFileByWebApi(fileBlob: Blob, filename: string) {
// 创建一个新句柄。
// @ts-ignore
const newHandle = await window.showSaveFilePicker({
suggestedName: filename,
});
// 创建一个 FileSystemWritableFileStream 用于写入。
const writableStream = await newHandle.createWritable();
// 写入我们的文件。
await writableStream.write(fileBlob);
// 关闭文件并将内容写入磁盘。
await writableStream.close();
}
const updatePageSize=(pageSize: number) => {
params.value.size = pageSize;
refreshData();
};
const refreshData=() => {
request({
url: '/admin/file/list',
method: 'get',
params: params.value,
}).then((res:any) => {
tableData.value = res.detail.data;
params.value.total = res.detail.total;
});
}
refreshData();
</script>
+155
View File
@@ -0,0 +1,155 @@
<template>
<div class="file-list">
<el-empty style="width: 90vw;" v-if="localFiles.length===0" description="请在/opt/filecodebox/local目录上传您需要分享的文件" />
<el-card v-for="file in localFiles" :key="file.name" class="file-card" shadow="hover">
<div class="file-info">
<div class="file-name">{{ file.file }}</div>
<div class="file-date">{{ file.ctime }}</div>
<div style="width: 100%;text-align: right">
<el-button type="primary" style="margin-top: 1rem" @click="shareLocalFile(file)" plain>分享</el-button>
<el-button type="danger" style="margin-top: 1rem" @click="deleteLocalFile(file)" plain>删除</el-button>
</div>
</div>
</el-card>
<el-dialog v-model="dialogFormVisible" width="500">
<el-form :model="form">
<el-form-item :label="t('admin.local.Name')" >
<el-input v-model="form.name" readonly autocomplete="off" />
</el-form-item>
<el-form-item :label="t('admin.local.Expire')" >
<el-input
v-model="form.expireValue"
style="width: 200px"
:placeholder="t('send.pleaseInputExpireValue')"
>
<template #prepend>
<el-select v-model="form.expireStyle" :placeholder="t('send.expireStyle')" style="width: 75px">
<el-option v-for="item in config.expireStyle" :key="item" :label="t(`send.expireData.${item}`)" :value="item" />
</el-select>
</template>
<template #append>
<span v-if="form.expireStyle==='day'">{{t('send.expireValue.day')}}</span>
<span v-else-if="form.expireStyle==='hour'">{{t('send.expireValue.hour')}}</span>
<span v-else-if="form.expireStyle==='minute'">{{t('send.expireValue.minute')}}</span>
<span v-else-if="form.expireStyle==='forever'">👌</span>
<span v-else-if="form.expireStyle==='count'">{{t('send.expireValue.count')}}</span>
</template>
</el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">{{ t('admin.local.Cancel') }}</el-button>
<el-button type="primary" @click="toShareFile()">
{{ t('admin.local.Confirm') }}
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import {ref, reactive} from 'vue';
import {request} from "@/utils/request";
import {ElMessage} from "element-plus";
import {useConfigStore} from "@/stores/config";
import {useI18n} from "vue-i18n";
const { t } = useI18n()
const { config } = useConfigStore();
const localFiles:any = ref([]);
const loadLocalFiles = ()=>{
request({
url: '/admin/local/lists',
method: 'get',
}).then((data:any) => {
localFiles.value = data.detail;
});
}
const dialogFormVisible = ref(false);
const form = reactive({
name: '1',
expireStyle: 'day',
expireValue: 1,
})
loadLocalFiles()
const deleteLocalFile = (file: any) => {
request({
url: '/admin/local/delete',
method: 'delete',
data: {
filename: file.file
}
}).then((data:any) => {
ElMessage.success(data.detail);
loadLocalFiles()
});
};
const shareLocalFile = (file:any) => {
form.name = file.file;
dialogFormVisible.value = true;
};
const toShareFile=()=>{
request({
url: '/admin/local/share',
method: 'post',
data: {
filename: form.name,
expire_style: form.expireStyle,
expire_value: form.expireValue
}
}).then((data:any) => {
dialogFormVisible.value = false;
ElMessage.success(
{
showClose: true,
message: 'Code:' + data.detail.code,
duration: 0
}
);
loadLocalFiles()
});
}
</script>
<style scoped>
.file-list {
display: grid;
width: 100%;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
padding: 16px;
}
.file-card {
align-items: center;
justify-content: space-between;
border-radius: 10px;
transition: all 0.3s ease;
}
.file-card:hover {
box-shadow: 0px 6px 15px rgba(0, 0, 0, 0.25);
}
.file-info {
flex: 1;
}
.file-name {
font-size: 14px;
font-weight: bold;
white-space: nowrap;
}
.file-date {
font-size: 12px;
color: #a0a0b2;
}
</style>
+219
View File
@@ -0,0 +1,219 @@
<template>
<el-form>
<el-form-item size="large" :label="t('admin.settings.name')">
<el-input v-model="config.name" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.description')">
<el-input v-model="config.description" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.keywords')" style="letter-spacing: 0.3rem">
<el-input v-model="config.keywords" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.themeSelect')" >
<el-select
v-model="config.themesSelect"
style="width: 100%"
>
<el-option v-for="item in config.themesChoices" :label="item.name" :value="item.key" />
</el-select>
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.notify_title')">
<el-input v-model="config.notify_title" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.notify_content')">
<el-input v-model="config.notify_content" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.background')">
<span style="display: flex;height: 38px;width: 45%">
<el-input v-model="config.background" placeholder="url" />
</span>
<span style="display: flex;height: 38px;margin-left: 20px">
Opacity: <el-input type="number" v-model="config.opacity" />
</span>
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.showAdminAddr')">
<el-select v-model="config.showAdminAddr" style="width: 80%">
<el-option :label="t('admin.settings.showAdmin.open')" :value="1" />
<el-option :label="t('admin.settings.showAdmin.close')" :value="0" />
</el-select>
<small style="margin-left: 0.4rem">{{ t('admin.settings.showAdmin.note') }}</small>
</el-form-item>
<el-form-item size="large" label="robots.Text">
<el-input type="textarea" v-model="config.robotsText" placeholder="explain" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.explain')">
<el-input type="textarea" v-model="config.page_explain" placeholder="explain" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.admin_token')">
<el-input type="password" v-model="config.admin_token" />
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.uploadSize')">
<el-input type="number" v-model="config.uploadSize" />
<template #append></template>
<small>Bytes{{ t('admin.settings.uploadSizeNote') }}</small>
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.max_save_seconds')">
<el-input type="number" v-model="config.max_save_seconds" />
<small>Seconds:{{t('admin.settings.maxSaveSecondsNote')}}</small>
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.expireStyle')" >
<el-select
v-model="config.expireStyle"
multiple
style="width: 100%"
>
<el-option :label="t('send.expireData.day')" value="day" />
<el-option :label="t('send.expireData.hour')" value="hour" />
<el-option :label="t('send.expireData.minute')" value="minute" />
<el-option :label="t('send.expireData.forever')" value="forever" />
<el-option :label="t('send.expireData.count')" value="count" />
</el-select>
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.openUpload.title')">
<el-select v-model="config.openUpload" style="width: 80%">
<el-option :label="t('admin.settings.openUpload.open')" :value="1" />
<el-option :label="t('admin.settings.openUpload.close')" :value="0" />
</el-select>
<small style="margin-left: 0.4rem">{{ t('admin.settings.openUpload.note') }}</small>
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.file_storage.title')">
<el-select v-model="config.file_storage" style="width: 80%">
<el-option :label="t('admin.settings.file_storage.local')" value="local" />
<el-option :label="t('admin.settings.file_storage.s3')" value="s3" />
</el-select>
<small style="margin-left: 0.4rem">{{ t('admin.settings.file_storage.note') }}</small>
</el-form-item>
<div v-if="config.file_storage==='s3'">
<el-form-item size="large" label="S3 AccessKeyId">
<el-input v-model="config.s3_access_key_id" />
</el-form-item>
<el-form-item size="large" label="S3 SecretAccessKey">
<el-input v-model="config.s3_secret_access_key" />
</el-form-item>
<el-form-item size="large" label="S3 BucketName">
<el-input v-model="config.s3_bucket_name" />
</el-form-item>
<el-form-item size="large" label="S3 EndpointUrl">
<el-input v-model="config.s3_endpoint_url" />
</el-form-item>
<el-form-item size="large" label="S3 hostname">
<el-input v-model="config.s3_hostname" />
</el-form-item>
<el-form-item size="large" label="S3 region name">
<el-input v-model="config.s3_region_name" />
</el-form-item>
<el-form-item size="large" label="S3 Signature Version">
<el-input v-model="config.s3_signature_version" />
</el-form-item>
<el-form-item size="large" label="Aws Session Token">
<el-input v-model="config.aws_session_token" />
</el-form-item>
</div>
<el-form-item size="large" :label="t('admin.settings.uploadlimit')">
<span style="display: flex;height: 38px">
<span style="margin-right: 0.4rem">{{ t('admin.settings.mei') }}</span>
<el-input type="number" v-model="config.uploadMinute" />
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.minute') }}</span>
</span>
<span style="display: flex;height: 38px">
<span style="width:3rem;margin-right: 0.4rem">{{ t('admin.settings.upload') }}</span>
<el-input type="number" v-model="config.uploadCount" />
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.files') }}</span>
</span>
</el-form-item>
<el-form-item size="large" :label="t('admin.settings.errorlimit')">
<span style="display: flex;height: 38px">
<span style="margin-right: 0.4rem">{{ t('admin.settings.mei') }}</span>
<el-input type="number" v-model="config.errorMinute" />
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.minute') }}</span>
</span>
<span style="display: flex;height: 38px">
<span style="width:3rem;margin-right: 0.4rem">{{ t('admin.settings.allow') }}</span>
<el-input type="number" v-model="config.errorCount" />
<span style="width: 200px;margin-left: 0.4rem">{{ t('admin.settings.errors') }}</span>
</span>
</el-form-item>
<el-form-item>
<el-button @click="submitSave" type="primary" style="margin: auto">{{ t('admin.settings.save') }}</el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import { request } from "@/utils/request";
import { ElMessage } from "element-plus";
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const config:any = ref({
name: '',
description: '',
file_storage: '',
expireStyle: [],
themesChoices: [],
themesSelect: '',
admin_token: '',
robotsText:'',
keywords: '',
notify_title: '',
notify_content: '',
openUpload: 1,
uploadSize: 1,
uploadMinute: 1,
max_save_seconds: 0,
opacity: 0.9,
s3_access_key_id: '',
background: '',
showAdminAddr: 0,
page_explain: '',
s3_secret_access_key: '',
aws_session_token: '',
s3_signature_version: '',
s3_region_name: '',
s3_bucket_name: '',
s3_endpoint_url: '',
s3_hostname: '',
uploadCount: 1,
errorMinute: 1,
errorCount: 1,
});
const refreshData = ()=>{
request({
url: '/admin/config/get',
method: 'get'
}).then((res: any) => {
config.value = res.detail;
});
}
refreshData();
const submitSave = () => {
request({
url: '/admin/config/update',
method: 'patch',
data: config.value
}).then((res: any) => {
if (res.code == 200) {
ElMessage.success(t('admin.settings.saveSuccess'));
} else {
ElMessage.error(res.message);
}
});
};
</script>
<style lang="scss">
.left-menu {
margin-bottom: 1rem;
cursor: pointer;
text-align: center;
font-weight: bold;
font-size: 1rem;
letter-spacing: 0.4rem;
}
small,.el-form-item__content{
color: #909399;
margin-left: 0.4rem;
}
</style>
+126
View File
@@ -0,0 +1,126 @@
<script setup lang="ts">
import { Upload, TakeawayBox } from '@element-plus/icons-vue';
import { onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from "vue-router";
import CardTools from "@/components/CardTools.vue";
import { useFileBoxStore } from "@/stores/fileBox";
import { useFileDataStore } from "@/stores/fileData";
import { ElMessageBox } from 'element-plus'
import { request } from "@/utils/request";
const fileBoxStore = useFileBoxStore();
const fileStore = useFileDataStore();
const router = useRouter()
const route = useRoute()
const code = ref('')
const input_status = reactive({
'readonly': false,
'loading': false,
})
import { useI18n } from 'vue-i18n'
import { useConfigStore } from "@/stores/config";
const { config } = useConfigStore()
const noDialog = ()=>{
ElMessageBox.alert(config.explain, t('send.disclaimers'), {
confirmButtonText: t('fileBox.ok'),
});
}
const { t } = useI18n()
onMounted(() => {
const query_code = route.query.code as string;
if (query_code) {
code.value = query_code
}
})
watch(code, (newVal) => {
if (newVal.length === 5) {
input_status.readonly = true;
input_status.loading = true;
request({
'url': '/share/select/',
'method': 'POST',
'data': {
'code': newVal
}
}).then((res: any) => {
fileBoxStore.showFileBox = true;
let flag = true;
fileStore.receiveData.forEach((file: any) => {
if (file.code === res.detail.code) {
flag = false;
return;
}
});
if (flag) {
fileStore.addReceiveData(res.detail);
}
}).finally(() => {
input_status.readonly = false;
input_status.loading = false;
code.value = '';
});
}
});
const listenInput = (num: number) => {
if (code.value.length < 5) {
code.value += num
}
};
const name = atob('RmlsZUNvZGVCb3ggVjIuMQ==');
const url = atob('aHR0cHM6Ly9naXRodWIuY29tL3Zhc3RzYS9GaWxlQ29kZUJveA==');
</script>
<template>
<main>
<el-card class="card" style="padding-bottom: 1rem">
<CardTools/>
<el-row style="text-align: center">
<el-col :span="24">
<el-input :readonly="input_status.readonly" v-loading="input_status.loading" v-model="code" class="code-input" round autofocus clearable maxlength="5" :placeholder="t('fileBox.inputNotEmpty')"/>
</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>
<div style="text-align: center; margin-top: 1rem;color: #606266">
<a style="text-decoration: none;color: #606266" target="_blank" :href="url">
{{ name }}
</a>
<a @click="noDialog" style="text-decoration: none;color: #606266;margin-left: 1rem" href="javascript:void(0)">{{t('send.disclaimers')}}</a>
<a v-if="config.show_admin_address" style="color: #606266;margin-left: 1rem;text-decoration: none" href="#/admin">Admin</a>
</div>
</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>
+59
View File
@@ -0,0 +1,59 @@
<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";
import { useI18n } from 'vue-i18n'
import { useConfigStore } from "@/stores/config";
const { config } = useConfigStore();
const { t } = useI18n()
const shareData = ref({
expireValue: 1,
expireStyle: config.expireStyle.length > 0 ? config.expireStyle[0] : 'day',
targetType: 'file',
})
</script>
<template>
<main>
<el-card class="card" style="position: relative" :body-style="{ padding: '0' }">
<card-tools style="padding: 1rem"/>
<div style="display: flex;margin: 1rem">
<div>
<el-input
v-model="shareData.expireValue"
style="width: 200px"
:placeholder="t('send.pleaseInputExpireValue')"
>
<template #prepend>
<el-select v-model="shareData.expireStyle" :placeholder="t('send.expireStyle')" style="width: 75px">
<el-option v-for="item in config.expireStyle" :key="item" :label="t(`send.expireData.${item}`)" :value="item" />
</el-select>
</template>
<template #append>
<span v-if="shareData.expireStyle=='day'">{{t('send.expireValue.day')}}</span>
<span v-else-if="shareData.expireStyle=='hour'">{{t('send.expireValue.hour')}}</span>
<span v-else-if="shareData.expireStyle=='minute'">{{t('send.expireValue.minute')}}</span>
<span v-else-if="shareData.expireStyle=='forever'">👌</span>
<span v-else-if="shareData.expireStyle=='count'">{{t('send.expireValue.count')}}</span>
</template>
</el-input>
</div>
<el-radio-group v-model="shareData.targetType" style="margin-left: 1rem;">
<el-radio label="file">
{{t('send.fileType.file')}}
</el-radio>
<el-radio label="text">
{{t('send.fileType.text')}}
</el-radio>
</el-radio-group>
</div>
<div style="margin: 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>