first commit
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user