feat: adding PC-upload, PC-search, PC-admin-register

This commit is contained in:
Wzp-2008 2023-12-03 21:51:13 +08:00
parent d095523e62
commit ef1832775a
2 changed files with 296 additions and 56 deletions

View File

@ -123,6 +123,20 @@ export const getLink = (id: number) => {
export const remove = (id: number) => {
return instance.post<Result<boolean>>("/api/file/remove", {id});
}
const handlerSearchType = (searchType: SearchType): string => {
switch (searchType){
case SearchType.ID:
return "ID"
case SearchType.TYPE:
return "TYPE"
case SearchType.UPLOAD_DAY:
return "UPLOAD_DAY"
case SearchType.UPLOADER:
return "UPLOADER"
default:
return "FILE_NAME"
}
}
/**
*
* @param page
@ -131,7 +145,7 @@ export const remove = (id: number) => {
* @param data
*/
export const search = (page: number, num: number, searchType: SearchType, data: string) => {
return instance.get<PageResult<File>>("/api/file/search", {params: {page, num, searchType, data}});
return instance.get<PageResult<File>>("/api/file/search", {params: {page, num, type: handlerSearchType(searchType), data}});
}
/**
* 访
@ -145,4 +159,11 @@ export const getAccessInformation = (count: number) => {
*/
export const getTotalAccessInformation = () => {
return instance.get<AccessInformation>("/api/access/getAll");
}
/**
*
* @param filename
*/
export const checkUpload = (filename: string) => {
return instance.get<Result<boolean>>("/api/file/checkUpload", {params: {filename}});
}

View File

@ -10,19 +10,20 @@
<!-- 顶部输入框 -->
<div>
<!-- 输入框属性与文本 -->
<el-input v-model="searchInput" placeholder="请输入要搜索的内容">
<el-input :type="searchInputType" v-model="searchInput" placeholder="请输入要搜索的内容">
<!-- 选择下拉框 -->
<template #prepend>
<el-select placeholder="名字" value-key="1" style="width: 100px">
<el-option label="名字" value="1"/>
<el-option label="ID" value="2"/>
<el-option label="MD5" value="3"/>
<el-option label="文件类型" value="4"/>
<el-select @change="searchTypeChange" v-model="searchType" style="width: 100px">
<el-option label="名字" :value="SearchType.FILE_NAME"/>
<el-option label="ID" :value="SearchType.ID"/>
<el-option label="上传者" :value="SearchType.UPLOADER"/>
<el-option label="文件类型" :value="SearchType.TYPE"/>
<el-option label="文件上传日期" :value="SearchType.UPLOAD_DAY"/>
</el-select>
</template>
<!-- 搜索图标与按钮 -->
<template #append>
<el-button :icon="Search"/>
<el-button :icon="Search" @click="handlerSearch"/>
</template>
</el-input>
</div>
@ -34,19 +35,34 @@
<!-- 用户登录弹窗 -->
<van-popup v-model:show="userAvatarLoginShow" round>
<!-- 登录/注册弹窗内容 -->
<div class="login-content">
<h2>登录/注册</h2>
<p>欢迎来到文件分享站</p>
<label for="userName">用户名</label>
<van-field id="userName" v-model="usernameInput" center placeholder="请输入用户名"/>
<div class="dialog">
<h2 v-if="isLoginMode">登录</h2>
<h2 v-else>注册</h2>
<p v-if="isLoginMode">欢迎回到文件分享站</p>
<p v-else>欢迎加入文件分享站</p>
<!-- 用户名输入框 -->
<label for="password">密码</label>
<van-field id="password" v-model="userPasswordInput" center type="password" placeholder="请输入用户密码"/>
<label for="username" class="login-dialog-element">用户名</label>
<el-input id="username" class="login-dialog-element" v-model="usernameInput" placeholder="请输入用户名" />
<!-- 密码输入框 -->
<label for="password" class="login-dialog-element">密码</label>
<el-input id="password" class="login-dialog-element" v-model="userPasswordInput" type="password" placeholder="请输入用户密码" />
<div v-if="!isLoginMode" class="login-dialog-element">
<label for="registerType">注册类型</label>
<el-select id="registerType" v-model="registerTypeInput" placeholder="注册类型">
<el-option key="1" label="普通用户" :value="Auth.USER"/>
<el-option key="2" label="管理员" :value="Auth.ADMIN"/>
</el-select>
<div v-if="registerTypeInput == Auth.ADMIN">
<label for="verifyCode" class="login-dialog-element">邀请码</label>
<el-input id="verifyCode" class="login-dialog-element" v-model="verifyCodeInput" type="password" placeholder="请输入管理员用户邀请码" />
</div>
</div>
<div class="actions">
<van-button class="user-login-register-button" @click="userLogin" type="primary">登录</van-button>
<van-button class="user-login-register-button" @click="userLogin" type="primary" v-if="isLoginMode">登录</van-button>
<van-button class="user-login-register-button" @click="userEnroll" type="primary" v-else>注册</van-button>
<!-- 登录按钮 -->
<van-button class="user-login-register-button" @click="userEnroll" type="primary">注册后登录</van-button>
<van-button class="user-login-register-button" @click="isLoginMode = false;" type="primary" v-if="isLoginMode">切换到注册</van-button>
<van-button class="user-login-register-button" @click="isLoginMode = true;" type="primary" v-else>切换到登录</van-button>
<!-- 注册按钮 -->
</div>
</div>
@ -54,7 +70,7 @@
<!-- 用户属性弹窗 -->
<van-popup v-model:show="userAvatarLoginShowTools" round>
<!-- 登录弹窗 -->
<div class="login-content">
<div class="dialog">
<h2>个人中心</h2>
<h3>用户属性:</h3>
<div>
@ -66,22 +82,26 @@
</p>
</div>
<h3>功能区:</h3>
<el-button type="primary" @click="genVerifyCode" v-if="userAuth == Auth.ADMIN">获取邀请码</el-button>
<el-button type="primary" @click="logout">退出登录</el-button>
</div>
</van-popup>
<!-- 文件详情 -->
<van-popup v-model:show="showFileInformation" round>
<div class="file-information-popover">
<h2>文件名称:<span>XXXXXXX</span></h2>
<div class="file-information-popover" v-if="nowSelectedFileInformation">
<h2>文件名称:<span>{{ nowSelectedFileInformation.name }}</span></h2>
<div>
<p>文件ID: <span>1</span></p>
<p>文件: <span>测试</span></p>
<p>文件类型: <span>exe</span></p>
<p>文件MD5: <span>hjs7g90shds7s09ydg9s07gs90g7s7g</span></p>
<p>文件上传者: <span>wzp</span></p>
<p>文件上传时间: <span>2022-07-12 16:21:39</span></p>
<p>文件ID: <span>{{ nowSelectedFileInformation.id }}</span></p>
<p>文件类型: <span>{{ nowSelectedFileInformation.type }}</span></p>
<p>文件大小: <span>{{ parserByteSize(nowSelectedFileInformation.size) }}</span></p>
<p title="点击复制"><a class="file-hash-a" @click="copyFullHash">文件校验值: <span>{{ shortHash(nowSelectedFileInformation.hash) }}</span></a></p>
<p>文件上传者: <span>{{ nowSelectedFileInformation.uploaderName }}</span></p>
<p>文件上传时间: <span>{{ nowSelectedFileInformation.uploadTime }}</span></p>
</div>
</div>
<div class="file-information-popover" v-else>
<h2>emm咱就是说这个分享站好像有点bug这个东西好像展示不出来捏:&lt;</h2>
</div>
</van-popup>
</div>
</el-col>
@ -102,10 +122,10 @@
<el-table-column prop="uploaderName" label="上传者" width="150"/>
<el-table-column fixed="right" label="操作" :width="fileOptionRowWidth">
<!-- 操作列模板 -->
<template #default>
<el-button link type="primary">下载</el-button>
<el-button link type="primary" @click="showFileInformationDialog">详情</el-button>
<el-button link type="danger" v-if="userAuth === Auth.ADMIN">删除</el-button>
<template #default="scope">
<el-button link type="primary" @click="downloadFile(scope.row.id)">下载</el-button>
<el-button link type="primary" @click="handlerFileInformation(scope.row)">详情</el-button>
<el-button link type="danger" v-if="userAuth === Auth.ADMIN" @click="removeFile(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -128,7 +148,10 @@
<el-upload
class="upload-demo"
drag
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
:http-request="handleUpload"
:before-remove="handleUploadRemove"
:on-success="handleUploadSuccess"
:before-upload="handleBeforeUpload"
multiple
>
<el-icon class="el-icon--upload">
@ -183,22 +206,37 @@
<script setup lang="ts">
import {Search, UploadFilled} from '@element-plus/icons-vue';
import 'element-plus/theme-chalk/dark/css-vars.css'
import {ref, onMounted, computed, onBeforeMount} from 'vue';
import {computed, onBeforeMount, onMounted, ref} from 'vue';
import {useDark, useToggle} from "@vueuse/core"
import {GridComponent} from 'echarts/components';
import {GridComponent, LegendComponent} from 'echarts/components';
import {LineChart} from 'echarts/charts';
import {UniversalTransition} from 'echarts/features';
import {CanvasRenderer} from 'echarts/renderers';
import {LegendComponent} from 'echarts/components';
import * as echarts from 'echarts/core';
import {getAllFiles, register, Result, getAccessInformation, getTotalAccessInformation} from "../api/Requester.ts";
import {
baseUrl,
checkUpload,
generatorVerifyCode,
getAccessInformation,
getAllFiles,
getLink,
getTotalAccessInformation,
register,
remove,
Result,
search,
upload
} from "../api/Requester.ts";
import {login} from '../api/Requester';
import {AxiosResponse} from "axios";
import {User} from "../entities/User.ts";
import {ElMessage} from "element-plus";
import {ElMessage, UploadFile, UploadRawFile, UploadRequestOptions} from "element-plus";
import {Auth} from "../entities/Auth.ts";
import {File} from "../entities/File.ts";
import {AccessInformation} from "../entities/AccessInformation.ts";
import {SearchType} from "../entities/SearchType.ts";
import {Awaitable} from "element-plus/es/utils";
import {UploadAjaxError} from "element-plus/es/components/upload/src/ajax";
echarts.use([GridComponent, LineChart, CanvasRenderer, UniversalTransition, LegendComponent]);
@ -212,22 +250,21 @@ const username = computed(() => {
})
const userAuth = computed(() => {
const authStorage = sessionStorage.getItem("auth");
if (authStorage) {
console.log(authStorage)
switch (authStorage) {
case "admin":
return Auth.ADMIN;
default:
return Auth.USER;
}
} else {
return "未登录"
switch (authStorage) {
case "admin":
return Auth.ADMIN;
case "user":
return Auth.USER;
default:
return "未登录";
}
})
// TODO:
//
const searchInput = ref<string>('');
const searchInput = ref<string | number>('');
//
const searchType = ref<SearchType>(SearchType.FILE_NAME);
//
const userAvatar = 'src/assets/userAvatar.jpg';
//
@ -237,6 +274,14 @@ const userPasswordInput = ref<string>("");
const userAvatarLoginShow = ref<boolean>(false);
//
const userAvatarLoginShowTools = ref<boolean>(false);
///
const isLoginMode = ref<boolean>(true);
//
const registerTypeInput = ref<Auth>(Auth.USER);
//
const verifyCodeInput = ref<string>("");
//
const searchInputType = ref<string>("text");
//
const isDark = useDark()
const toggleDark = useToggle(isDark);
@ -267,7 +312,22 @@ const fileOptionRowWidth = computed(() => {
const currentPage = ref<number>(1);
const totalFileCount = ref<number>(0);
const pageFileData = ref<File[]>([]);
const isSearchType = ref<boolean>(false);
const handlerSearch = () => {
isSearchType.value = true;
showAllFiles();
}
const showAllFiles = () => {
isSearchType.value = searchInput.value !== '';
if (isSearchType.value){
search(currentPage.value, 20, searchType.value, searchInput.value).then((res) => {
totalFileCount.value = res.data.data.total;
pageFileData.value = res.data.data.data;
}).catch(() => {
ElMessage.error("搜索失败!");
})
return;
}
getAllFiles(currentPage.value, 20).then((res) => {
totalFileCount.value = res.data.data.total;
pageFileData.value = res.data.data.data;
@ -288,6 +348,18 @@ const parserByteSize = (byte: number): string => {
}
return (byte / 1024 / 1024 / 1024 / 1024).toFixed(2) + "TB";
}
const shortHash = (hash: string): string => {
return hash.substring(0,10) + '......' + hash.substring(118,128)
}
const copyFullHash = () => {
if (nowSelectedFileInformation.value){
navigator.clipboard.writeText(nowSelectedFileInformation.value.hash);
ElMessage.success("复制成功!");
}else {
ElMessage.error("你是怎么点到这个按钮的???");
}
}
//
const handlerPageChange = (val: number) => {
currentPage.value = val;
@ -378,7 +450,7 @@ const allContrastDownload = contrastDownload;
// TODO:
//
const imgLoginBox = () => {
if (sessionStorage.getItem("authorization")) {
if (isLogin.value) {
//
//
userAvatarLoginShowTools.value = true;
@ -433,7 +505,7 @@ const userEnroll = () => {
ElMessage.error("用户名密码不能为空")
return;
}
register(usernameInput.value, userPasswordInput.value, Auth.USER).then((res) => {
register(usernameInput.value, userPasswordInput.value, registerTypeInput.value, verifyCodeInput.value).then((res) => {
switch (res.data.status) {
case 251:
ElMessage.error("邀请码错误!");
@ -442,23 +514,164 @@ const userEnroll = () => {
ElMessage.error("存在同名用户!");
break;
case 200:
ElMessage.success("注册成功");
sessionStorage.setItem("username", res.data.data.name);
sessionStorage.setItem("auth", res.data.data.auth.toString());
setTimeout(() => {
imgLoginBox();
}, 1000);
ElMessage.success("注册成功");
window.location.reload()
break;
default:
ElMessage.error("无法注册!");
}
});
}
//
const showFileInformationDialog = () => {
showFileInformation.value = true
/**
* 获取管理员验证码
*/
const genVerifyCode = () => {
if (userAuth.value != Auth.ADMIN){
ElMessage.error("呜~你是怎么发现这个按钮的,可惜哦~不能用哦~")
return;
}
generatorVerifyCode().then((res: AxiosResponse<Result<string>>) => {
ElMessage.success("已成功获取到邀请码:" + res.data.data + "已复制到剪切板有效期15分钟");
navigator.clipboard.writeText(res.data.data);
})
}
// TODO: onMounted
/**
* 当搜索类型切换
*/
const searchTypeChange = () => {
switch (searchType.value){
case SearchType.ID:
searchInputType.value = "number";
break;
case SearchType.UPLOAD_DAY:
searchInputType.value = "date";
break;
default:
searchInputType.value = "text";
break;
}
}
const abortControllers = ref<Map<string, AbortController>>(new Map());
//
const isLogin = computed(() => sessionStorage.getItem("authorization") != null)
/**
* 处理上传任务删除
* @param uploadFile 上传的文件
*/
const handleUploadRemove = (uploadFile: UploadFile): Awaitable<boolean> => {
if (uploadFile.status !== "uploading"){
return true;
}
const name = uploadFile.raw?.name as string;
const controller = abortControllers.value.get(name);
if (controller){
controller.abort("user");
ElMessage.success("成功取消!")
return true;
}
return false;
}
/**
* 处理上传成功
* @param _ 无用变量
* @param uploadFile 上传的文件
*/
const handleUploadSuccess = (_: never, uploadFile: UploadFile): void => {
ElMessage.success("文件" + uploadFile.raw?.name + "上传成功!");
}
/**
* 上传前的预处理
* @param rawFile 上传的文件
*/
const handleBeforeUpload = (rawFile: UploadRawFile): boolean => {
if (!isLogin.value){
ElMessage.error("未登录,无法上传!");
return false;
}
if (abortControllers.value.has(rawFile.name)){
ElMessage.error("文件:" + rawFile.name + "上传失败,已存在同名文件正在上传")
return false;
}
return new Promise((resolve, reject) => {
checkUpload(rawFile.name).then((res: AxiosResponse<Result<boolean>>) => {
if (res.data.data) {
resolve();
} else {
ElMessage.error("文件:" + rawFile.name + "上传失败,已存在同名文件");
reject();
}
})
});
}
/**
* 处理上传请求
* @param options 上传设置包括上传的文件等
*/
const handleUpload = (options: UploadRequestOptions): XMLHttpRequest | Promise<unknown> => {
const abortController = new AbortController();
abortControllers.value.set(options.file.name, abortController);
let promise = upload(options, abortController);
promise.then((response: AxiosResponse<Result<File>>) => {
if (response.data.status == 200){
options.onSuccess(response);
}else if(response.data.status == 401){
ElMessage.error("文件:" + options.file.name + "上传失败,文件已存在!");
options.onError(new UploadAjaxError("文件已存在", 401, option.method, options.action));
}
}).catch((error) => {
options.onError(error)
}).finally(() => {
abortControllers.value.delete(options.file.name);
});
return promise;
}
// TODO:
const nowSelectedFileInformation = ref<File>();
/**
* 展示文件详细信息
* @param file 文件信息
*/
const handlerFileInformation = (file: File) => {
showFileInformation.value = true;
nowSelectedFileInformation.value = file;
}
/**
* 下载文件
* @param id 文件id
*/
const downloadFile = (id: number) => {
getLink(id).then((res: AxiosResponse<Result<string>>) => {
window.open(baseUrl + "/api/file/download/" + res.data.data);
})
}
/**
* 删除文件
* @param id 文件id
*/
const removeFile = (id: number) => {
if (userAuth.value != Auth.ADMIN){
ElMessage.error("呜~你是怎么发现这个按钮的,可惜哦~不能用哦~")
return;
}
remove(id).then((res: AxiosResponse<Result<boolean>>) => {
if (res.data.data){
ElMessage.success("删除成功!");
showAllFiles()
return;
}
ElMessage.error("删除失败,请查看后台日志处理!");
});
}
// TODO:
onBeforeMount(() => {
initTheme();
})
// TODO: onMounted
onMounted(() => {
window.addEventListener("resize", changeFigureSize);
fiveData();
@ -537,9 +750,9 @@ img {
line-height: 1.3;
}
.login-content {
.dialog {
width: 45vh;
height: 30vh;
height: max-content;
margin: 20px;
top: 50%;
left: 50%;
@ -552,4 +765,10 @@ img {
.file-information-popover {
margin: 20px;
}
.file-hash-a:hover {
cursor: pointer;
}
.login-dialog-element {
margin-top: 5px;
}
</style>