Compare commits

..

No commits in common. "main" and "API" have entirely different histories.
main ... API

22 changed files with 602 additions and 1427 deletions

2
.gitignore vendored
View File

@ -24,5 +24,3 @@ dist-ssr
*.sw?
/keys
/dist
dist.zip

2
auto-imports.d.ts vendored
View File

@ -5,7 +5,5 @@
// Generated by unplugin-auto-import
export {}
declare global {
const ElMess: typeof import('element-plus/es')['ElMess']
const ElMessaeg: typeof import('element-plus/es')['ElMessaeg']
const ElMessage: typeof import('element-plus/es')['ElMessage']
}

4
components.d.ts vendored
View File

@ -7,6 +7,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
@ -16,15 +17,16 @@ declare module 'vue' {
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElUpload: typeof import('element-plus/es')['ElUpload']
ElWatermark: typeof import('element-plus/es')['ElWatermark']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanButton: typeof import('vant/es')['Button']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanConfigProvider: typeof import('vant/es')['ConfigProvider']
VanField: typeof import('vant/es')['Field']
VanImage: typeof import('vant/es')['Image']
VanPopup: typeof import('vant/es')['Popup']

View File

@ -1,41 +0,0 @@
// main.ts
// 控制应用生命周期和创建原生浏览器窗口的模组
import path from "path";
import {app, BrowserWindow} from "electron";
function createWindow() {
// 创建浏览器窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// 引入预加载文件
preload: path.join(__dirname, "preload.js"),
},
});
// 加载vite启动的本地服务
mainWindow.loadURL("http://localhost:5173");
}
// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(() => {
createWindow();
app.on("activate", function () {
// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
// 打开的窗口,那么程序会重新创建一个窗口。
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
app.on("window-all-closed", function () {
if (process.platform !== "darwin") app.quit();
});

View File

@ -1 +0,0 @@
console.log("preload");

View File

@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/UserAvatar.jpg" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>文件分享站</title>
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>

25
package-lock.json generated
View File

@ -11,8 +11,7 @@
"@types/crypto-js": "^4.2.1",
"axios": "^1.6.2",
"crypto-js": "^4.2.0",
"echarts": "^5.4.3",
"element-plus": "^2.4.3",
"element-plus": "^2.4.2",
"vant": "^4.7.3",
"vue": "^3.3.4",
"vue-router": "^4.2.5"
@ -1591,15 +1590,6 @@
"node": ">=6.0.0"
}
},
"node_modules/echarts": {
"version": "5.4.3",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz",
"integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==",
"dependencies": {
"tslib": "2.3.0",
"zrender": "5.4.4"
}
},
"node_modules/element-plus": {
"version": "2.4.3",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.4.3.tgz",
@ -2887,11 +2877,6 @@
"typescript": ">=4.2.0"
}
},
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
@ -3312,14 +3297,6 @@
"engines": {
"node": ">=10"
}
},
"node_modules/zrender": {
"version": "5.4.4",
"resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz",
"integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==",
"dependencies": {
"tslib": "2.3.0"
}
}
}
}

View File

@ -3,19 +3,16 @@
"private": true,
"version": "0.0.0",
"type": "module",
"main": "dist/main.js",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"electron:dev":"tsc && electron ."
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@types/crypto-js": "^4.2.1",
"axios": "^1.6.2",
"crypto-js": "^4.2.0",
"echarts": "^5.4.3",
"element-plus": "^2.4.3",
"element-plus": "^2.4.2",
"vant": "^4.7.3",
"vue": "^3.3.4",
"vue-router": "^4.2.5"
@ -26,7 +23,6 @@
"@typescript-eslint/parser": "^6.11.0",
"@vant/auto-import-resolver": "^1.0.2",
"@vitejs/plugin-vue": "^4.2.3",
"electron": "^27.1.3",
"eslint": "^8.54.0",
"eslint-plugin-vue": "^9.18.1",
"typescript": "^5.0.2",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -11,7 +11,7 @@ const onInitOrResize = () => {
}
}
onMounted(() => {
window.addEventListener("resize", onInitOrResize);
window.onresize = onInitOrResize;
onInitOrResize();
})
</script>

View File

@ -5,10 +5,8 @@ import {md5Hex} from "./CryptoUtils";
import {User} from "../entities/User";
import {Auth} from "../entities/Auth";
import {File} from "../entities/File";
import {SearchType} from "../entities/SearchType.ts";
import {AccessInformation} from "../entities/AccessInformation.ts";
export const baseUrl = '';
export const baseUrl = 'http://localhost:8081';
export interface Result<T> {
data: T;
@ -28,7 +26,7 @@ const instance = axios.create({
timeout: 1000,
headers: {'Content-Type': 'application/json'}
});
instance.interceptors.request.use(function (config) {
axios.interceptors.request.use(function (config) {
config.headers['Authorization'] = window.sessionStorage.getItem("authorization");
return config;
}, function (error) {
@ -37,8 +35,8 @@ instance.interceptors.request.use(function (config) {
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
const authorization = response.headers['set-authorization'];
axios.interceptors.response.use(function (response) {
const authorization = response.headers['Set-Authorization'];
if (authorization) {
window.sessionStorage.setItem("authorization", authorization);
}
@ -46,24 +44,12 @@ instance.interceptors.response.use(function (response) {
}, function (error) {
return Promise.reject(error);
});
/**
*
* @param username
* @param password
*/
export const login = (username: string, password: string) => {
return instance.post<Result<User>>("/api/user/login", {
name: username,
password: md5Hex(password)
})
}
/**
*
* @param username
* @param password
* @param auth Auth.user或Auth.admin
* @param verifyCode Auth.user则无需填写
*/
export const register = (username: string, password: string, auth: Auth, verifyCode?: string) => {
return instance.post<Result<User>>("/api/user/register", {
name: username,
@ -72,26 +58,13 @@ export const register = (username: string, password: string, auth: Auth, verifyC
verifyCode: verifyCode,
})
}
/**
*
*/
export const generatorVerifyCode = () => {
return instance.get<Result<string>>("/api/user/verifyCode")
}
/**
*
* @param page
* @param num
*/
// 获取文件
export const getAllFiles = (page: number, num: number) => {
return instance.get<PageResult<File>>("/api/file/getAll", {params: {num, page}});
}
/**
*
* @param options el-upload的http-request方法调用时传入
* @param controller
*/
// 上传文件
export const upload = (options: UploadRequestOptions, controller: AbortController) => {
const param = new FormData();
param.append("file", options.file);
@ -109,61 +82,3 @@ export const upload = (options: UploadRequestOptions, controller: AbortControlle
signal: controller.signal
})
}
/**
*
* @param id id
*/
export const getLink = (id: number) => {
return instance.get<Result<string>>("/api/file/link", {params: {id}});
}
/**
*
* @param id id
*/
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
* @param num
* @param searchType
* @param data
*/
export const search = (page: number, num: number, searchType: SearchType, data: string) => {
return instance.get<PageResult<File>>("/api/file/search", {params: {page, num, type: handlerSearchType(searchType), data}});
}
/**
* 访
* @param count
*/
export const getAccessInformation = (count: number) => {
return instance.get<AccessInformation[]>("/api/access/get", {params: {count}});
}
/**
* 访
*/
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}});
}

BIN
src/assets/UserAvatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,17 +0,0 @@
/**
* 访
*/
export interface AccessInformation {
/**
*
*/
totalDownload: number;
/**
* 访
*/
totalAccess: number;
/**
*
*/
date: string;
}

View File

@ -1,13 +1,3 @@
/**
*
*/
export enum Auth {
/**
*
*/
USER,
/**
*
*/
ADMIN,
user, admin
}

View File

@ -1,37 +1,10 @@
/**
*
*/
export interface File {
/**
*
*/
downloadCount: number;
/**
* hashsha512
*/
hash: string;
/**
* id
*/
id: number;
/**
*
*/
name: string;
/**
*
*/
size: number;
/**
*
*/
type: string;
/**
*
*/
uploadTime: string;
/**
*
*/
uploaderName: string;
}

View File

@ -1,25 +0,0 @@
/**
*
*/
export enum SearchType {
/**
*
*/
FILE_NAME,
/**
* ID
*/
ID,
/**
*
*/
UPLOADER,
/**
*
*/
UPLOAD_DAY,
/**
*
*/
TYPE
}

View File

@ -1,19 +1,7 @@
import {Auth} from "./Auth";
/**
*
*/
export interface User {
/**
* id
*/
id: number;
/**
*
*/
id: string
name: string;
/**
*
*/
auth: Auth;
}

View File

@ -3,7 +3,6 @@ import './style.css'
import App from './App.vue'
import Mobile from './views/AppMobile.vue'
import PC from './views/AppPC.vue'
import 'element-plus/dist/index.css';
import {createRouter, createWebHashHistory} from "vue-router";
const router = createRouter({

View File

@ -1,380 +1,156 @@
<template>
<van-config-provider :theme="vantTheme"></van-config-provider>
<div class="MobileHome">
<!-- 页面主体部分 -->
<div class="head">
<!-- 头部区域 -->
<van-cell-group inset class="inputFileName">
<!-- 搜索框区域 -->
<van-field v-model="searchInput" center clearable placeholder="请输入文件名称">
<van-field v-model="inputFileName" center clearable placeholder="请输入文件名称">
<!-- 文件名输入框 -->
<template #button>
<!-- 搜索按钮 -->
<van-button size="small" type="primary" @click="handlerSearch">搜索</van-button>
<van-button size="small" type="primary">搜索</van-button>
</template>
</van-field>
</van-cell-group>
<!-- 用户头像 -->
<van-image
class="userAvatar"
width="50"
height="50"
:src="userAvatar"
src="src/assets/UserAvatar.jpg"
radius="5"
@click="imgLoginBox"
@click="toggleUserLogin"
/>
<van-popup v-model:show="userAvatarLoginShow" round position="top">
<!-- 登录/注册弹窗内容 -->
<div class="dialog">
<h2 v-if="isLoginMode">登录</h2>
<h2 v-else>注册</h2>
<p v-if="isLoginMode">欢迎回到文件分享站</p>
<p v-else>欢迎加入文件分享站</p>
<!-- 用户头像 -->
<van-popup v-model:show="userAvatarLoginShow" round position="top" class="userLoginBox">
<!-- 登录弹窗 -->
<div class="login-content">
<h2>登录/注册</h2>
<p>欢迎来到文件分享站</p>
<label for="userName">用户名</label>
<van-field id="userName" v-model="userName" center placeholder="请输入用户名"/>
<!-- 用户名输入框 -->
<label for="username" class="login-dialog-element">用户名</label>
<el-input id="username" class="login-dialog-element" v-model="usernameInput" placeholder="请输入用户名"/>
<label for="password">密码</label>
<van-field id="password" v-model="userPassword" center type="password" 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" v-if="isLoginMode">
登录
</van-button>
<van-button class="user-login-register-button" @click="userEnroll" type="primary" v-else>注册
</van-button>
<van-button class="userLoginButton" @click="userLogin" 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>
<van-button class="userLoginButton" @click="userEnroll" type="primary">注册后登录</van-button>
<!-- 注册按钮 -->
</div>
</div>
</van-popup>
<!-- 属性弹窗 -->
<van-popup v-model:show="userAvatarLoginShowTools" round position="top" class="userLoginBox">
<div class="dialog">
<!-- 登录弹窗 -->
<div class="login-content">
<h2>个人中心</h2>
<h3>用户属性:</h3>
<div>
<p>用户名称 {{ username }}</p>
<p>用户类型:
<span v-if="userAuth === Auth.ADMIN">管理员</span>
<span v-else-if="userAuth === Auth.USER">普通用户</span>
<p class="userNameId">用户ID: {{ userNameToolsId }} </p>
<p class="userNameName">用户名称: {{ userNameToolsName }}</p>
<p class="userNameType">用户类型:
<span v-if="userNameToolsAuth === 'admin'">管理员</span>
<span v-else-if="userNameToolsAuth === 'user'">普通用户</span>
<span v-else>未登录</span>
</p>
</div>
<h3>功能区</h3>
<h3>功能区:</h3>
<div class="uploadFile">
<div class="button-container" style="display: flex;">
<el-upload
class="upload-demo"
v-model:file-list="fileList"
class="upload"
action="http://127.0.0.1:8081/api/file/upload"
multiple
:http-request="handleUpload"
:before-remove="handleUploadRemove"
:on-success="handleUploadSuccess"
:before-upload="handleBeforeUpload"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:limit="3"
:on-exceed="handleExceed"
>
<el-button type="primary">点击上传文件</el-button>
</el-upload>
<el-button type="primary" @click="genVerifyCode" v-if="userAuth == Auth.ADMIN">获取邀请码</el-button>
<el-button type="primary" @click="logout">退出登录</el-button>
<template #tip>
<div class="el-upload__tip">
最大文件限制大小:10GB
</div>
</template>
</el-upload>
</div>
</div>
</van-popup>
<!-- 文件详情 -->
<van-popup v-model:show="showFileInformation" round>
<div class="file-information-popover" v-if="nowSelectedFileInformation">
<h2>文件名称:<br><span>{{ nowSelectedFileInformation.name }}</span></h2>
<div>
<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>
<!-- 文件详情 -->
<van-popup v-model:show="showFileInformation" round>
<div class="file-information-popover" v-if="nowSelectedFileInformation">
<h2>文件名称:<span>{{ nowSelectedFileInformation.name }}</span></h2>
<div>
<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>
<div class="fileLiteBox">
<el-table ref="fileListTable" :data="pageFileData" class="files-data-table" border stripe>
<el-table-column prop="id" label="ID" width="70"/>
<el-table-column prop="name" label="文件名称" width="650"/>
<el-table-column prop="type" label="文件格式" width="120"/>
<el-table-column label="文件大小" width="120">
<template #default="scope">{{ parserByteSize(scope.row.size) }}</template>
</el-table-column>
<el-table-column prop="uploaderName" label="上传者" width="120"/>
<el-table-column fixed="right" label="操作" :width="fileOptionRowWidth">
<!-- 操作列模板 -->
<el-table :data="nowFileData" border style="width: 100%">
<el-table-column prop="id" label="ID" width="60"/>
<el-table-column prop="name" label="文件名" width="200"/>
<el-table-column prop="type" label="文件格式" width="150"/>
<el-table-column prop="size" label="文件大小" width="100"/>
<el-table-column prop="uploaderName" label="上传者" width="100"/>
<el-table-column fixed="right" label="操作" width="100">
<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 link type="primary" size="small" @click="handleShowDetail();">
<!-- 显示详细信息按钮 -->
详细
</el-button>
<el-button link type="primary" size="small" @click="handleDownload();">下载</el-button>
<!-- 下载按钮 -->
<el-button link type="primary" size="small" @click="handleDelete();" v-if="isAdmin">删除
<!-- 删除按钮仅管理员可见 -->
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<div class="pagination-container">
<!-- 分页控件容器 -->
<el-pagination
@current-change="handlerPageChange"
@current-change="fetchFiles"
:current-page="currentPage"
:page-sizes="[19]"
:page-size="19"
layout="prev, pager, next"
:total="totalFileCount"
class="pagination"
/>
:page-size="pageSize"
:total="totalFiles"
layout="prev, pager, next">
</el-pagination>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import 'element-plus/theme-chalk/dark/css-vars.css'
import {computed, onBeforeMount, onMounted, ref} from 'vue';
import {useDark, useToggle} from "@vueuse/core"
import {GridComponent, LegendComponent} from 'echarts/components';
import {LineChart} from 'echarts/charts';
import {UniversalTransition} from 'echarts/features';
import {CanvasRenderer} from 'echarts/renderers';
import * as echarts from 'echarts/core';
import {
baseUrl,
checkUpload,
generatorVerifyCode,
getAllFiles,
getLink,
register,
remove,
Result,
search,
upload
} from "../api/Requester.ts";
import {login} from '../api/Requester';
import {onMounted, ref} from "vue";
import {ElMessage} from "element-plus";
import 'element-plus/es/components/message/style/css';
import {login, register, getAllFiles, Result} from '../api/Requester';
import {User} from "../entities/User";
import {AxiosResponse} from "axios";
import {User} from "../entities/User.ts";
import {UploadFile, UploadRawFile, UploadRequestOptions, ElMessage} from "element-plus";
import {Auth} from "../entities/Auth.ts";
import {File} from "../entities/File.ts";
import {Awaitable} from "element-plus/es/utils";
import {UploadAjaxError} from "element-plus/es/components/upload/src/ajax";
import {SearchType} from "../entities/SearchType.ts";
import {Auth} from "../entities/Auth";
import {File} from "../entities/File";
echarts.use([GridComponent, LineChart, CanvasRenderer, UniversalTransition, LegendComponent]);
// TODO:
const inputFileName = ref<string>('');
const userAvatarLoginShow = ref<boolean>(false);
const userAvatarLoginShowTools = ref<boolean>(false);
const userName = ref<string>("");
const userPassword = ref<string>("");
const isAdmin = ref<boolean>(false); //
// TODO:
const username = computed(() => {
const username = sessionStorage.getItem("username");
if (username) {
return username;
}
return "未登录"
})
const userAuth = computed(() => {
const authStorage = sessionStorage.getItem("auth");
switch (authStorage) {
case "admin":
return Auth.ADMIN;
case "user":
return Auth.USER;
default:
return "未登录";
}
})
const userNameToolsId = ref<number>(0);
const userNameToolsName = ref<string>("未登录");
const userNameToolsAuth = ref<string>("未登录");
// TODO:
//
const searchType = ref<SearchType>(SearchType.FILE_NAME);
const searchInput = ref<string>("");
//
const userAvatar = 'UserAvatar.jpg'
//
const usernameInput = ref<string>('');
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 nowSelectedFileInformation = ref<File>();
//
const handlerFileInformation = (file: File) => {
showFileInformation.value = true;
nowSelectedFileInformation.value = file;
}
//
const downloadFile = (id: number) => {
getLink(id).then((res: AxiosResponse<Result<string>>) => {
window.open(baseUrl + "/api/file/download/" + res.data.data);
})
}
const removeFile = (id: number) => {
if (userAuth.value != Auth.ADMIN) {
ElMessage.error({
message: '呜~你是怎么发现这个按钮的,可惜哦~不能用哦~',
grouping: true,
})
return;
}
remove(id).then((res: AxiosResponse<Result<boolean>>) => {
if (res.data.data) {
ElMessage.success({
message: '删除成功!',
grouping: true,
});
showAllFiles()
return;
}
ElMessage.error({
message: '删除失败,请查看后台日志处理!',
grouping: true,
});
});
}
//
const isDark = useDark()
const toggleDark = useToggle(isDark);
const vantTheme = ref<"light" | "dark">("light");
//
const initTheme = () => {
const now = new Date();
const hour = now.getHours();
if (hour < 7 || hour > 17) {
toggleDark(true);
vantTheme.value = "dark";
return;
}
toggleDark(false);
}
// TODO:
const showFileInformation = ref<boolean>(false);
// admin user UI
const fileOptionRowWidth = computed(() => {
switch (userAuth.value) {
case Auth.ADMIN:
return 155;
default:
return 110;
}
});
//
// TODO:
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 = () => {
if (isSearchType.value) {
search(currentPage.value, 19, searchType.value, searchInput.value).then((res) => {
totalFileCount.value = res.data.data.total;
pageFileData.value = res.data.data.data;
}).catch(() => {
ElMessage.error({
message: '搜索失败!',
grouping: true,
});
})
return;
}
getAllFiles(currentPage.value, 19).then((res) => {
totalFileCount.value = res.data.data.total;
pageFileData.value = res.data.data.data;
})
}
const parserByteSize = (byte: number): string => {
if (byte < 1024) {
return byte + 'B';
}
if (byte < 1024 * 1024) {
return (byte / 1024).toFixed(2) + "KB";
}
if (byte < 1024 * 1024 * 1024) {
return (byte / 1024 / 1024).toFixed(2) + "MB";
}
if (byte < 1024 * 1024 * 1024 * 1024) {
return (byte / 1024 / 1024 / 1024).toFixed(2) + "GB";
}
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({
message: '复制成功!',
grouping: true,
});
} else {
ElMessage.error({
message: '你是怎么点到这个按钮的???',
grouping: true,
});
}
const pageSize = ref<number>(18);
const nowFileData = ref<File[]>([]);
const totalFiles = ref<number>(0);
}
//
const handlerPageChange = (val: number) => {
currentPage.value = val;
showAllFiles();
};
// TODO:
//
const imgLoginBox = () => {
if (isLogin.value) {
// TODO:
const toggleUserLogin = () => {
if (sessionStorage.getItem("userInfo") || localStorage.getItem("userInfo")) {
//
//
userAvatarLoginShowTools.value = true;
@ -384,211 +160,217 @@ const imgLoginBox = () => {
userAvatarLoginShow.value = !userAvatarLoginShow.value;
userAvatarLoginShowTools.value = false;
}
}
//
};
// TODO:
const logout = () => {
//
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
//
window.location.reload();
};
// TODO:
const userLogin = () => {
// cookie
sessionStorage.clear();
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
//
if (usernameInput.value == "" || userPasswordInput.value == "") {
ElMessage.error({
message: '用户名密码不能为空',
grouping: true,
});
if (userName.value == "" || userPassword.value == "") {
ElMessage.error("用户名密码不能为空");
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
return;
}
//
login(usernameInput.value, userPasswordInput.value)
login(userName.value, userPassword.value)
.then((res: AxiosResponse<Result<User>>) => {
//
if (res.data.status == 200) {
const username = res.data.data.name;
sessionStorage.setItem("username", username);
sessionStorage.setItem("auth", res.data.data.auth.toString());
//
// sessionStorage
const userInfo = {
userName: userName.value,
userPassword: userPassword.value,
};
sessionStorage.setItem("userInfo", JSON.stringify(userInfo));
console.log(res.data.data.name, res.data.data.id, res.data.data.auth);
userNameToolsId.value = Number(res.data.data.id);
userNameToolsName.value = String(res.data.data.name);
userNameToolsAuth.value = String(res.data.data.auth)
//
setTimeout(() => {
imgLoginBox();
toggleUserLogin();
}, 1000); // 10001
ElMessage.success({
message: '登录成功!',
grouping: true,
});
window.location.reload()
ElMessage.success("登录成功!");
} else {
ElMessage.error({
message: '登录失败,用户名密码错误!',
grouping: true,
});
ElMessage.error("登录失败,用户名密码错误!");
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
}
})
.catch(() => {
ElMessage.error("登录失败,用户名密码错误!");
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
});
};
//
const logout = () => {
//
ElMessage.success({
message: '退出成功!',
grouping: true,
//
let checkedStorage = false;
const checkLocalStorageUserInfo = () => {
if (checkedStorage) {
return; //
}
const userInfoString = localStorage.getItem("userInfo");
if (userInfoString) {
//
const userInfo = JSON.parse(userInfoString);
userName.value = userInfo.userName;
userPassword.value = userInfo.userPassword;
//
verifyUserInfo();
}
};
// sessionStorage
const checkSessionStorageUserInfo = () => {
if (checkedStorage) {
return; //
}
const userInfoString = sessionStorage.getItem("userInfo");
if (userInfoString) {
//
const userInfo = JSON.parse(userInfoString);
userName.value = userInfo.userName;
userPassword.value = userInfo.userPassword;
//
verifyUserInfo();
}
};
//
const verifyUserInfo = () => {
if (userName.value === "" || userPassword.value === "") {
return; //
}
login(userName.value, userPassword.value)
.then((res: AxiosResponse<Result<User>>) => {
if (res.data.status === 200) {
console.log(res.data.data.name, res.data.data.id, res.data.data.auth);
userNameToolsId.value = Number(res.data.data.id);
userNameToolsName.value = String(res.data.data.name);
userNameToolsAuth.value = String(res.data.data.auth)
ElMessage.success("登录成功!");
userAvatarLoginShowTools.value = true; //
userAvatarLoginShow.value = false; //
} else {
ElMessage.error("验证失败,请重新登录!");
userAvatarLoginShowTools.value = false; //
userAvatarLoginShow.value = true; //
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
}
})
//
sessionStorage.clear();
userAvatarLoginShowTools.value = false;
window.location.reload()
.catch(() => {
ElMessage.error("验证失败,请重新登录!");
userAvatarLoginShowTools.value = false; //
userAvatarLoginShow.value = true; //
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
});
};
//
// TODO:
const userEnroll = () => {
//
if (usernameInput.value == '' || userPasswordInput.value == '') {
ElMessage.error({
message: '用户名密码不能为空',
grouping: true,
})
if (userName.value == '' || userPassword.value == '') {
ElMessage.error("用户名密码不能为空")
return;
}
register(usernameInput.value, userPasswordInput.value, registerTypeInput.value, verifyCodeInput.value).then((res) => {
register(userName.value, userPassword.value, Auth.user).then((res) => {
switch (res.data.status) {
case 251:
ElMessage.error({
message: '邀请码错误!',
grouping: true,
});
ElMessage.error("邀请码错误!");
break;
case 501:
ElMessage.error({
message: '存在同名用户!',
grouping: true,
});
ElMessage.error("存在同名用户!");
break;
case 200:
ElMessage.success({
message: '注册成功',
grouping: true,
ElMessage.success("注册成功");
login(userName.value, userPassword.value)
.then((res: AxiosResponse<Result<User>>) => {
if (res.data.status === 200) {
console.log(res.data.data.name, res.data.data.id, res.data.data.auth);
userNameToolsId.value = Number(res.data.data.id);
userNameToolsName.value = String(res.data.data.name);
userNameToolsAuth.value = String(res.data.data.auth)
ElMessage.success("登录成功!");
userAvatarLoginShowTools.value = true; //
userAvatarLoginShow.value = false; //
} else {
ElMessage.error("验证失败,请重新登录!");
userAvatarLoginShowTools.value = false; //
userAvatarLoginShow.value = true; //
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
}
})
.catch(() => {
ElMessage.error("验证失败,请重新登录!");
userAvatarLoginShowTools.value = false; //
userAvatarLoginShow.value = true; //
localStorage.removeItem("userInfo");
sessionStorage.removeItem("userInfo");
});
sessionStorage.setItem("username", res.data.data.name);
sessionStorage.setItem("auth", res.data.data.auth.toString());
setTimeout(() => {
imgLoginBox();
}, 1000);
window.location.reload()
break;
default:
ElMessage.error({
message: '无法注册!',
grouping: true,
});
ElMessage.error("无法注册!");
}
});
}
/**
* 获取管理员验证码
*/
const genVerifyCode = () => {
if (userAuth.value != Auth.ADMIN) {
ElMessage.error({
message: '呜~你是怎么发现这个按钮的,可惜哦~不能用哦~',
grouping: true,
})
return;
}
generatorVerifyCode().then((res: AxiosResponse<Result<string>>) => {
ElMessage.success("已成功获取到邀请码:" + res.data.data + "已复制到剪切板有效期15分钟");
navigator.clipboard.writeText(res.data.data);
})
}
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({
message: '成功取消!',
grouping: true,
})
return true;
}
return false;
}
/**
* 处理上传成功
* @param _ 无用变量
* @param uploadFile 上传的文件
*/
const handleUploadSuccess = (_: never, uploadFile: UploadFile): void => {
ElMessage.success("文件" + uploadFile.raw?.name + "上传成功!");
}
/**
* 上传前的预处理
* @param rawFile 上传的文件
*/
const handleBeforeUpload = (rawFile: UploadRawFile): Promise<unknown> => {
return new Promise((resolve, reject) => {
if (!isLogin.value) {
ElMessage.error({
message: '未登录,无法上传!',
grouping: true,
});
reject();
}
if (abortControllers.value.has(rawFile.name)) {
ElMessage.error("文件:" + rawFile.name + "上传失败,已存在同名文件正在上传")
reject();
}
checkUpload(rawFile.name).then((res: AxiosResponse<Result<boolean>>) => {
if (res.data.data) {
resolve(res.data.data);
} 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) {
return;
}
if (response.data.status == 401) {
ElMessage.error("文件:" + options.file.name + "上传失败,文件已存在!");
options.onError(new UploadAjaxError("文件已存在", 401, options.method, options.action));
return;
}
ElMessage.error("文件:" + options.file.name + "上传失败," + response.data.message);
options.onError(new UploadAjaxError(response.data.message, response.data.status, options.method, options.action));
}).catch((error) => {
options.onError(error)
}).finally(() => {
abortControllers.value.delete(options.file.name);
});
return promise;
}
// TODO:
onBeforeMount(() => {
initTheme();
// TODO:
const fetchFiles = (current?: number) => {
if (current) {
currentPage.value = current;
}
getAllFiles(currentPage.value, pageSize.value).then((res) => {
nowFileData.value = res.data.data.data;
totalFiles.value = res.data.data.total;
})
}
// TODO: onMounted
// TODO:
// TODO:
const handleShowDetail = () => {
console.log("查看文件属性接口");
}
// TODO:
const handleDownload = () => {
console.log("download");
}
// TODO:
const handleDelete = () => {
console.log("delete");
}
// TODO:
onMounted(() => {
showAllFiles();
fetchFiles();
checkSessionStorageUserInfo();
checkLocalStorageUserInfo();
checkedStorage = true;
})
</script>
<style scoped>
@ -596,6 +378,7 @@ onMounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f0f0f0; /* 头部样式,包括布局和背景颜色 */
}
.inputFileName {
@ -612,6 +395,7 @@ onMounted(() => {
}
.userLoginBox {
background-color: #fff;
border-radius: 10px;
padding: 20px; /* 登录弹窗样式 */
}
@ -635,6 +419,10 @@ onMounted(() => {
margin-top: 20px; /* 按钮区域样式 */
}
.userLoginButton {
width: 120px; /* 登录注册按钮样式 */
}
.fileLiteBox {
margin-top: 10px;
}
@ -647,18 +435,4 @@ onMounted(() => {
text-align: center; /* 文本水平居中对齐 */
margin-top: 10px;
}
.dialog {
margin: 20px;
}
.file-information-popover {
margin: 20px;
}
.button-container {
display: flex;
justify-content: space-between; /* 或其他值如 center, space-around 等 */
gap: 10px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,9 @@
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": false,
"noEmit": false,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"outDir": "dist",
/* Linting */
"strict": true,
@ -21,13 +20,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"auto-imports.d.ts",
"electron/*.ts"
],
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -24,9 +24,7 @@ export default defineConfig({
}),
Components({
resolvers: [
ElementPlusResolver({
importStyle: "css", // 确保样式也被自动导入
}),
ElementPlusResolver(),
VantResolver(),
],
}),