项目完成
This commit is contained in:
commit
50fc0045e7
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# 多模态文件管理系统前端工程 filemanage-frontend
|
||||||
|
|
||||||
|
## 初始化项目
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自定义项目配置
|
||||||
|
1.修改main.js下的后端api请求路径前缀BASE_URL
|
||||||
|
|
||||||
|
2.修改vue.config.js下的前端工程运行端口port
|
||||||
|
|
||||||
|
## 开发环境下启动
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打包工程到生产环境部署
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
19
jsconfig.json
Normal file
19
jsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "esnext",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
20320
package-lock.json
generated
Normal file
20320
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
47
package.json
Normal file
47
package.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"name": "filemanage-frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
|
"axios": "^1.6.7",
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"element-plus": "^2.6.1",
|
||||||
|
"vue": "^3.2.13",
|
||||||
|
"vue-router": "^4.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.16",
|
||||||
|
"@babel/eslint-parser": "^7.12.16",
|
||||||
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-plugin-vue": "^8.0.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@babel/eslint-parser"
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead",
|
||||||
|
"not ie 11"
|
||||||
|
]
|
||||||
|
}
|
BIN
public/background.png
Normal file
BIN
public/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 464 KiB |
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
18
public/index.html
Normal file
18
public/index.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<!-- <title><%= htmlWebpackPlugin.options.title %></title>-->
|
||||||
|
<title>多模态文件管理系统</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
23
src/App.vue
Normal file
23
src/App.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 样式可以根据需要修改 */
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
</style>
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
307
src/components/FilePage.vue
Normal file
307
src/components/FilePage.vue
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 文件上传表单 -->
|
||||||
|
<el-upload
|
||||||
|
class="upload-demo"
|
||||||
|
:action="uploadUrl"
|
||||||
|
:with-credentials="true"
|
||||||
|
:on-success="handleUploadSuccess"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
drag
|
||||||
|
>
|
||||||
|
<el-button size="small" type="primary">点击上传</el-button>
|
||||||
|
<template v-slot:tip>
|
||||||
|
<div class="el-upload__tip">或将文件拖到此处上传</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-divider></el-divider>
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-form ref="searchForm" :model="searchForm" >
|
||||||
|
<el-row>
|
||||||
|
<el-input v-model="searchForm.name" placeholder="请输入要查询的文件名" style="width: 240px; padding-right: 10px"></el-input>
|
||||||
|
<el-button type="primary" @click="getFiles" :icon="Search"></el-button>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<!-- 批量操作按钮 -->
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" @click="handleBatchDownload">批量下载</el-button>
|
||||||
|
<el-button type="danger" @click="handleBatchDelete">批量删除</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 图片预览模态框 -->
|
||||||
|
<el-dialog :title="previewImage.name" v-model="previewVisible" width="50%" @close="handleCloseDialog" center>
|
||||||
|
<img :src="previewImage.image" style="width: 100%" alt="预览图片" />
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 文件列表 -->
|
||||||
|
<el-table
|
||||||
|
:data="files"
|
||||||
|
border
|
||||||
|
:v-loading="loading"
|
||||||
|
:default-sort="{ prop: 'name', order: 'ascending' }"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
:show-overflow-tooltip=true
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column label="序号" type="index" />
|
||||||
|
<el-table-column label="文件名" prop="name" sortable>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<span v-if="row.type === 0" @click="getPreviewImage(row)" style="color: blue">{{ row.name }}</span>
|
||||||
|
<span v-else>{{ row.name }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="文件大小" prop="size" sortable>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{getFileSize(row.size)}}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="type" label="文件类型" sortable>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{ getFileType(row.type) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最后更新时间" prop="updateTime" sortable></el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-button type="primary" size="small" @click="downloadFile(row)">下载</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-pagination
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[5, 10, 20, 50]"
|
||||||
|
:total="total"
|
||||||
|
:background="true"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {BASE_URL} from "@/main";
|
||||||
|
import axios from "axios";
|
||||||
|
import {ElMessage, ElMessageBox} from "element-plus";
|
||||||
|
import { Search } from "@element-plus/icons-vue"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
Search() {
|
||||||
|
return Search
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
total: 0,
|
||||||
|
|
||||||
|
selectedRows: [], // 存储选中的行数据
|
||||||
|
searchForm: {name: ''},
|
||||||
|
files: [], // 所有文件数据
|
||||||
|
loading: false, // 加载状态
|
||||||
|
|
||||||
|
previewVisible: false,
|
||||||
|
previewImage: {
|
||||||
|
name: '',
|
||||||
|
image: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadUrl: BASE_URL+'/file/upload',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleCloseDialog() {
|
||||||
|
this.previewVisible =false;
|
||||||
|
this.previewImage = {
|
||||||
|
name: '',
|
||||||
|
image: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
//图片预览
|
||||||
|
getPreviewImage(file) {
|
||||||
|
axios.get(BASE_URL + '/file/preview/' + file.id, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.code === 1) {
|
||||||
|
this.previewVisible = true;
|
||||||
|
this.previewImage.name = file.name;
|
||||||
|
this.previewImage.image = response.data.data;
|
||||||
|
} else {
|
||||||
|
ElMessage.error('预览失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 多选框选中状态变化时
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.selectedRows = selection; // 更新选中的行数据
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBatchDownload() {
|
||||||
|
this.selectedRows.forEach(item => {
|
||||||
|
this.downloadFile(item);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleBatchDelete() {
|
||||||
|
let ids = [];
|
||||||
|
this.selectedRows.forEach(item => {
|
||||||
|
ids.push(item.id);
|
||||||
|
});
|
||||||
|
axios.put(BASE_URL+'/file'+'/1', ids, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1){
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
} else {
|
||||||
|
ElMessage.error('文件批量删除失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
this.getFiles();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//console.log(error);
|
||||||
|
ElMessage.error('请求失败:'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUploadSuccess() {
|
||||||
|
this.getFiles();
|
||||||
|
},
|
||||||
|
beforeUpload() {
|
||||||
|
// 在这里可以添加一些文件上传前的逻辑,比如文件大小限制等
|
||||||
|
return true; // 返回 true 表示允许上传
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理每页显示条数变化
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pageSize = size;
|
||||||
|
this.currentPage = 1; // 重置当前页码为1
|
||||||
|
this.getFiles();
|
||||||
|
},
|
||||||
|
// 处理当前页码变化
|
||||||
|
handleCurrentChange(page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
this.getFiles();
|
||||||
|
},
|
||||||
|
handleDelete(file) {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'确认要删除该文件吗(移入回收站)?',
|
||||||
|
'Warning',
|
||||||
|
{
|
||||||
|
confirmButtonText: '是',
|
||||||
|
cancelButtonText: '否',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.logicDeleteFile(file);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取文件列表数据
|
||||||
|
getFiles() {
|
||||||
|
this.loading = true; // 显示加载状态
|
||||||
|
let url = BASE_URL + '/file/page/'+this.currentPage+'/'+this.pageSize+'/0';
|
||||||
|
axios.get(url, {params:{name: this.searchForm.name}, withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.code === 1) {
|
||||||
|
// this.files = response.data.data; // 设置文件列表数据
|
||||||
|
this.files = response.data.data.records; // 设置文件列表数据
|
||||||
|
this.total = response.data.data.total;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//console.error('文件列表获取失败', error);
|
||||||
|
ElMessage.error('文件列表获取失败,'+error);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false; // 隐藏加载状态
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getFileSize(size) {
|
||||||
|
if(size/1024 < 1.0)
|
||||||
|
return size.toFixed(1)+'B';
|
||||||
|
if(size/(1024*1024) < 1.0)
|
||||||
|
return (size/(1024)).toFixed(1)+'KB';
|
||||||
|
if(size/(1024*1024*1024) < 1.0)
|
||||||
|
return (size/(1024*1024)).toFixed(1)+'MB';
|
||||||
|
return (size/(1024*1024*1024)).toFixed(1)+'GB';
|
||||||
|
},
|
||||||
|
getFileType(type) {
|
||||||
|
// 根据文件类型代码返回对应的文字描述
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
return '图片';
|
||||||
|
case 1:
|
||||||
|
return '视频';
|
||||||
|
case 2:
|
||||||
|
return '音频';
|
||||||
|
case 3:
|
||||||
|
return '文档';
|
||||||
|
default:
|
||||||
|
return '其他';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
downloadFile(file) {
|
||||||
|
// 下载文件
|
||||||
|
axios.get(BASE_URL+'/file/download/'+file.id, {
|
||||||
|
responseType: 'blob',
|
||||||
|
withCredentials: true
|
||||||
|
}).then(response => {
|
||||||
|
const url = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute('download', file.name);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
// 下载完成后销毁 URL 对象
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}).catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
if(error.response.status === 404){
|
||||||
|
ElMessage.error('文件'+file.name+'下载失败,文件不存在或被删除');
|
||||||
|
this.getFiles();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ElMessage.error("请求失败:"+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
logicDeleteFile(file) {
|
||||||
|
// 删除文件的操作
|
||||||
|
//console.log('删除文件:', file);
|
||||||
|
axios.put(BASE_URL+'/file/'+file.id+'/1', {}, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1){
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
} else {
|
||||||
|
ElMessage.error('文件'+file.name+'删除失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
this.getFiles();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//console.log(error);
|
||||||
|
ElMessage.error('请求失败:'+error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
126
src/components/HomePage.vue
Normal file
126
src/components/HomePage.vue
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home-container">
|
||||||
|
<!-- 左上角个人信息简要和退出按钮 -->
|
||||||
|
<div class="user-info">
|
||||||
|
<span style="padding-right: 5px">{{ currentUser.username }}</span>
|
||||||
|
<el-button @click="logout" plain>退出</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 左侧菜单 -->
|
||||||
|
<el-aside width="200px" style="margin-top: 50px;">
|
||||||
|
<el-menu :default-active="currentMenu" class="el-menu-vertical-demo" @select="handleMenuSelect">
|
||||||
|
<el-menu-item index="myFiles">我的文件</el-menu-item>
|
||||||
|
<el-menu-item index="profile">个人中心</el-menu-item>
|
||||||
|
<el-menu-item index="recycleBin">回收站</el-menu-item>
|
||||||
|
<el-menu-item index="management" v-if="currentUser.isAdmin === 1">管理</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-aside>
|
||||||
|
|
||||||
|
<!-- 主内容区 -->
|
||||||
|
<el-main>
|
||||||
|
<!-- 根据菜单切换展示不同内容 -->
|
||||||
|
<div v-if="currentMenu === 'myFiles'">
|
||||||
|
<FilePage/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="currentMenu === 'profile'">
|
||||||
|
<ProfilePage :currentUser="currentUser" @update:currentUser="updateUsername"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="currentMenu === 'recycleBin'">
|
||||||
|
<!-- 回收站内容 -->
|
||||||
|
<RecycleBinPage/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="currentMenu === 'management'">
|
||||||
|
<ManagementPage/>
|
||||||
|
</div>
|
||||||
|
</el-main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import {BASE_URL} from "@/main";
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import ProfilePage from '@/components/ProfilePage.vue'
|
||||||
|
import ManagementPage from '@/components/ManagementPage.vue'
|
||||||
|
import FilePage from '@/components/FilePage.vue'
|
||||||
|
import RecycleBinPage from '@/components/RecycleBinPage.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ProfilePage,
|
||||||
|
ManagementPage,
|
||||||
|
FilePage,
|
||||||
|
RecycleBinPage
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentUser: {
|
||||||
|
email: '',
|
||||||
|
username: '',
|
||||||
|
isAdmin: 0,
|
||||||
|
storageUsed: 0,
|
||||||
|
storageTotal: 0
|
||||||
|
}, // 从服务端获取的用户信息
|
||||||
|
currentMenu: 'myFiles', // 当前选中的菜单
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleMenuSelect(index) {
|
||||||
|
this.currentMenu = index;
|
||||||
|
},
|
||||||
|
|
||||||
|
getUser() {
|
||||||
|
axios.get(BASE_URL+'/user', {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1) {
|
||||||
|
this.currentUser = response.data.data;
|
||||||
|
} else
|
||||||
|
ElMessage.error('用户信息获取失败,'+response.data.msg);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
// 实现登出逻辑,例如清除本地存储的用户信息等
|
||||||
|
axios.post(BASE_URL+'/user/logout', {}, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1)
|
||||||
|
this.$router.push({name: 'Login'})
|
||||||
|
else ElMessage.error('退出失败,'+response.data.msg)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败:'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
//接受子组件传递的数据更新
|
||||||
|
updateUsername(newUsername){
|
||||||
|
this.currentUser.username = newUsername;
|
||||||
|
},
|
||||||
|
updateCloudStorageConfig(newConfig){
|
||||||
|
this.cloudStorageConfig = newConfig;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
148
src/components/LoginPage.vue
Normal file
148
src/components/LoginPage.vue
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="background"></div>
|
||||||
|
<div class="content">
|
||||||
|
<h1 class="welcome-title">欢迎使用文件管理系统</h1>
|
||||||
|
<el-form :model="loginForm" :rules="rules">
|
||||||
|
<el-form-item label="邮箱/用户名" prop="email">
|
||||||
|
<el-input v-model="loginForm.email" placeholder="请输入邮箱/用户名"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码" prop="password">
|
||||||
|
<el-input type="password" v-model="loginForm.password" placeholder="请输入密码"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="验证码" prop="captcha">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="16">
|
||||||
|
<el-input v-model="loginForm.captcha" placeholder="请输入验证码"></el-input>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<img :src="captchaImageUrl" @click="refreshCaptcha" alt="验证码" style="cursor: pointer; width: 130px; height: 48px;">
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" class="login-button" @click="login">登录</el-button>
|
||||||
|
<el-button @click="goToRegister" plain>注册</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ElForm, ElFormItem, ElInput, ElButton, ElRow, ElCol, ElMessage } from 'element-plus';
|
||||||
|
import axios from "axios";
|
||||||
|
import { BASE_URL } from "@/main";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ElForm,
|
||||||
|
ElFormItem,
|
||||||
|
ElInput,
|
||||||
|
ElButton,
|
||||||
|
ElRow,
|
||||||
|
ElCol
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.refreshCaptcha();
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loginForm: {
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
captcha: '',
|
||||||
|
verKey: ''
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
email: [
|
||||||
|
{ required: true, message: '请输入邮箱', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入密码', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
captcha: [
|
||||||
|
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
captchaImageUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
login() {
|
||||||
|
if(!this.loginForm.email || !this.loginForm.password || !this.loginForm.captcha){
|
||||||
|
ElMessage.error('请填写必填项');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//console.log('登录', this.loginForm);
|
||||||
|
axios.post(BASE_URL + '/user/login', this.loginForm, {withCredentials:true})
|
||||||
|
.then(response => {
|
||||||
|
// 请求成功
|
||||||
|
if (response.data.code === 1) {
|
||||||
|
//console.log('登录成功', response.data);
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
this.$router.push({name:'Home'});
|
||||||
|
} else if (response.data.code === 0) {
|
||||||
|
//console.log('登录失败', response.data);
|
||||||
|
ElMessage.error('登录失败,' + response.data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
// 请求失败
|
||||||
|
ElMessage.error('请求失败:'+ error);
|
||||||
|
// 根据错误信息进行处理,比如提示用户或者重试等
|
||||||
|
});
|
||||||
|
},
|
||||||
|
refreshCaptcha() {
|
||||||
|
// 发送请求获取新的图片验证码
|
||||||
|
axios.get(BASE_URL + '/user/captcha')
|
||||||
|
.then(response => {
|
||||||
|
let data = JSON.parse(response.data.data);
|
||||||
|
this.loginForm.verKey = data.key;
|
||||||
|
this.captchaImageUrl = data.image;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('验证码获取异常:'+error);
|
||||||
|
});
|
||||||
|
//console.log('刷新验证码');
|
||||||
|
},
|
||||||
|
goToRegister() {
|
||||||
|
//console.log('跳转到注册页面');
|
||||||
|
this.$router.push({name:'Registry'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url('/public/background.png');
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 400px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
304
src/components/ManagementPage.vue
Normal file
304
src/components/ManagementPage.vue
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
<template>
|
||||||
|
<h3 style="text-align: left">新增存储策略配置</h3>
|
||||||
|
<div style="text-align: left">
|
||||||
|
<el-radio-group v-model="storageType" @change="handleChange">
|
||||||
|
<!-- <el-radio :value="1">服务端本地存储</el-radio>-->
|
||||||
|
<el-radio :value="2">云存储(腾讯云)</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<!-- <el-button type="primary" @click="handleSave">新增</el-button>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="configs"
|
||||||
|
border
|
||||||
|
:v-loading="loading"
|
||||||
|
:show-overflow-tooltip=true
|
||||||
|
>
|
||||||
|
<el-table-column label="序号" type="index" />
|
||||||
|
<el-table-column label="存储策略名" prop="name" sortable width="120"></el-table-column>
|
||||||
|
<el-table-column label="策略类型" prop="type" sortable width="120">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{getConfigType(row.type)}}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="配置信息" prop="config" sortable></el-table-column>
|
||||||
|
<el-table-column label="最后更新时间" prop="updateTime" sortable></el-table-column>
|
||||||
|
<el-table-column label="创建者" prop="creator" sortable width="140"></el-table-column>
|
||||||
|
<el-table-column label="更新者" prop="updater" sortable width="140"></el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-button v-if="row.isActive === 0" type="primary" size="small" @click="updateConfigStatus(row)">启用</el-button>
|
||||||
|
<el-button type="warning" size="small" @click="handleUpdate(row)">修改</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
title="腾讯云存储配置"
|
||||||
|
v-model="dialogVisible"
|
||||||
|
@close="handleCloseDialog"
|
||||||
|
>
|
||||||
|
<!-- 在对话框中输入配置项 -->
|
||||||
|
<el-form :model="formConfig" label-width="120px" :rules="rules">
|
||||||
|
<el-form-item label="存储策略名" prop="name">
|
||||||
|
<el-input v-model="formConfig.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Secret Id" prop="secretId">
|
||||||
|
<el-input v-model="formConfig.secretId"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Secret Key" prop="secretKey">
|
||||||
|
<el-input v-model="formConfig.secretKey"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Region Name" prop="regionName">
|
||||||
|
<el-input v-model="formConfig.regionName"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Bucket Name" prop="bucketName">
|
||||||
|
<el-input v-model="formConfig.bucketName"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Custom Url">
|
||||||
|
<el-input v-model="formConfig.customUrl"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<p> 自2024年1月1日起,腾讯云对象存储新创建的存储桶不支持使用存储桶默认域名(包括存储桶域名、静态网站域名、全球加速域名)在浏览器预览文件,而是直接下载。<a href="https://cloud.tencent.com/document/product/436/96243" target="_blank" referrerpolicy="unsafe-url">了解更多</a></p>
|
||||||
|
<p> 存储桶默认域名存在安全风险,可能暴露敏感信息,建议您为存储桶 配置自定义域名。详情请参见 <a href="https://cloud.tencent.com/document/product/436/102509" target="_blank" referrerpolicy="unsafe-url">存储桶切换自定义域名</a></p>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="saveCloudConfig">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
title="存储配置修改"
|
||||||
|
v-model="updateDialogVisible"
|
||||||
|
@close="handleCloseUpdateDialog"
|
||||||
|
>
|
||||||
|
<!-- 在对话框中输入配置项 -->
|
||||||
|
<el-form :model="formConfig" label-width="120px" :rules="rules">
|
||||||
|
<el-form-item label="存储策略名" prop="name">
|
||||||
|
<el-input v-model="formConfig.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Secret Id" prop="secretId">
|
||||||
|
<el-input v-model="formConfig.secretId" :disabled="true" @copy.prevent></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Secret Key" prop="secretKey">
|
||||||
|
<el-input v-model="formConfig.secretKey" :disabled="true" @copy.prevent></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Region Name" prop="regionName">
|
||||||
|
<el-input v-model="formConfig.regionName" :disabled="true" @copy.prevent></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Bucket Name" prop="bucketName">
|
||||||
|
<el-input v-model="formConfig.bucketName" :disabled="true" @copy.prevent></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Custom Url">
|
||||||
|
<el-input v-model="formConfig.customUrl"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="updateDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="updateStorageConfig">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
import {ElMessage, ElMessageBox} from "element-plus";
|
||||||
|
import {BASE_URL} from "@/main";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formConfig: {
|
||||||
|
name: '',
|
||||||
|
secretId: '',
|
||||||
|
secretKey: '',
|
||||||
|
regionName: '',
|
||||||
|
bucketName: '',
|
||||||
|
type: 1,
|
||||||
|
customUrl: ''
|
||||||
|
},
|
||||||
|
configs: [],
|
||||||
|
loading: false, // 加载状态
|
||||||
|
storageType: 1, // 默认选中服务端本地存储
|
||||||
|
dialogVisible: false, // 控制对话框显示隐藏
|
||||||
|
updateDialogVisible: false,// 控制修改对话框显示隐藏
|
||||||
|
updateId: '',//修改配置的id
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入存储策略名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
secretId: [
|
||||||
|
{ required: true, message: '请输入Secret Id', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
secretKey: [
|
||||||
|
{ required: true, message: '请输入Secret Key', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
regionName: [
|
||||||
|
{ required: true, message: '请输入Region Name', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
bucketName: [
|
||||||
|
{ required: true, message: '请输入Bucket Name', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getStorageConfigs() {
|
||||||
|
this.loading = true;
|
||||||
|
axios.get(BASE_URL+'/oss', {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.code === 1) {
|
||||||
|
this.configs = response.data.data;
|
||||||
|
} else {
|
||||||
|
ElMessage.error('配置信息获取失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getConfigType(type) {
|
||||||
|
if(type === 0)
|
||||||
|
return '阿里云';
|
||||||
|
else if(type === 1)
|
||||||
|
return '腾讯云';
|
||||||
|
else if(type === 2)
|
||||||
|
return '七牛云';
|
||||||
|
else return '本地';
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChange(val) {
|
||||||
|
if (val === 2) {
|
||||||
|
this.dialogVisible = true; // 显示对话框
|
||||||
|
this.formConfig.type = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// handleSave() {
|
||||||
|
// if (this.storageType === 2) {
|
||||||
|
// this.saveCloudConfig();
|
||||||
|
// } else {
|
||||||
|
// // 如果选择服务端本地存储,直接保存
|
||||||
|
// this.saveStorageConfig();
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
handleCloseDialog() {
|
||||||
|
// 关闭对话框时重置配置项
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.storageType = 1;
|
||||||
|
this.formConfig = {};
|
||||||
|
},
|
||||||
|
handleCloseUpdateDialog() {
|
||||||
|
// 关闭修改对话框时重置配置项
|
||||||
|
this.updateDialogVisible = false;
|
||||||
|
this.formConfig = {};
|
||||||
|
this.updateId = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDelete(row) {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'确认要删除该存储配置吗?',
|
||||||
|
'Warning',
|
||||||
|
{
|
||||||
|
confirmButtonText: '是',
|
||||||
|
cancelButtonText: '否',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.deleteConfig(row);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteConfig(row) {
|
||||||
|
axios.delete(BASE_URL+'/oss/'+row.id, {withCredentials:true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1){
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
this.getStorageConfigs();
|
||||||
|
} else {
|
||||||
|
ElMessage.error('存储配置 '+row.name+' 删除失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败:'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUpdate(row) {
|
||||||
|
this.updateId = row.id;
|
||||||
|
this.formConfig.name = row.name;
|
||||||
|
if(row.config !== null) {
|
||||||
|
let config = JSON.parse(row.config);
|
||||||
|
this.formConfig.secretId = config.secretId;
|
||||||
|
this.formConfig.secretKey = config.secretKey;
|
||||||
|
this.formConfig.regionName = config.regionName;
|
||||||
|
this.formConfig.bucketName = config.bucketName;
|
||||||
|
this.formConfig.customUrl = config.customUrl;
|
||||||
|
}
|
||||||
|
this.updateDialogVisible = true;
|
||||||
|
},
|
||||||
|
updateStorageConfig() {
|
||||||
|
// 发送云存储配置到后端保存
|
||||||
|
axios.put(BASE_URL+'/oss/'+this.updateId, this.formConfig, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1) {
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
this.getStorageConfigs();
|
||||||
|
} else {
|
||||||
|
ElMessage.error('修改存储配置失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
this.handleCloseUpdateDialog();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateConfigStatus(row) {
|
||||||
|
axios.put(BASE_URL+'/oss/'+row.id+'/'+row.isActive, {}, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1){
|
||||||
|
this.getStorageConfigs();
|
||||||
|
} else {
|
||||||
|
ElMessage.error('修改存储配置失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCloudConfig() {
|
||||||
|
// 发送云存储配置到后端保存
|
||||||
|
axios.post(BASE_URL+'/oss', this.formConfig, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1) {
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
this.getStorageConfigs();
|
||||||
|
} else {
|
||||||
|
ElMessage.error('保存云存储配置失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
this.handleCloseDialog();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getStorageConfigs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
176
src/components/ProfilePage.vue
Normal file
176
src/components/ProfilePage.vue
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 个人中心内容 -->
|
||||||
|
<div>
|
||||||
|
<h3 style="text-align: left">个人信息</h3>
|
||||||
|
<p style="text-align: left">用户名:{{ currentUser.username }}</p>
|
||||||
|
<p style="text-align: left">邮箱:{{ currentUser.email }}</p>
|
||||||
|
<p style="text-align: left">当前使用存储空间:{{getSize(this.currentUser.storageUsed) + " / " + getSize(this.currentUser.storageTotal)}} </p>
|
||||||
|
</div>
|
||||||
|
<!-- 修改密码表单 -->
|
||||||
|
<div>
|
||||||
|
<h3 style="text-align: left">修改密码</h3>
|
||||||
|
<el-form ref="passwordForm" :model="passwordForm" :rules="rules" label-width="80px" style="max-width: 400px;">
|
||||||
|
<el-form-item label="旧密码" prop="oldPassword" >
|
||||||
|
<el-input v-model="passwordForm.oldPassword" type="password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="新密码" prop="newPassword">
|
||||||
|
<el-input v-model="passwordForm.newPassword" type="password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="确认密码" prop="confirmPassword">
|
||||||
|
<el-input v-model="passwordForm.confirmPassword" type="password"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="updatePassword">提交</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 修改用户名表单 -->
|
||||||
|
<div>
|
||||||
|
<h3 style="text-align: left">修改用户名</h3>
|
||||||
|
<el-form ref="usernameForm" :rules="rules" :model="usernameForm" label-width="80px" style="max-width: 400px;">
|
||||||
|
<el-form-item label="新用户名" prop="username">
|
||||||
|
<el-input v-model="usernameForm.username"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="updateUsername">提交</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
import axios from "axios";
|
||||||
|
import {BASE_URL} from "@/main";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
currentUser: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
passwordForm: {
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: ''
|
||||||
|
},
|
||||||
|
usernameForm: {
|
||||||
|
username: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
username: [{ required: true, message: '请输入新用户名', trigger: 'blur' }],
|
||||||
|
oldPassword: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
|
||||||
|
newPassword: [{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === this.passwordForm.oldPassword) {
|
||||||
|
callback(new Error('新密码不能与旧密码相同'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
confirmPassword: [{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === this.passwordForm.newPassword) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
callback(new Error('两次输入的密码不一致'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getSize(size) {
|
||||||
|
if(size/1024 < 1.0)
|
||||||
|
return size.toFixed(1)+'B';
|
||||||
|
if(size/(1024*1024) < 1.0)
|
||||||
|
return (size/(1024)).toFixed(1)+'KB';
|
||||||
|
if(size/(1024*1024*1024) < 1.0)
|
||||||
|
return (size/(1024*1024)).toFixed(1)+'MB';
|
||||||
|
return (size/(1024*1024*1024)).toFixed(1)+'GB';
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePassword() {
|
||||||
|
if (!this.passwordForm.oldPassword || !this.passwordForm.newPassword || !this.passwordForm.newPassword) {
|
||||||
|
ElMessage.error('请填写必填项');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this.passwordForm.newPassword !== this.passwordForm.confirmPassword){
|
||||||
|
ElMessage.error('两次输入的密码不一致');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this.passwordForm.newPassword === this.passwordForm.oldPassword){
|
||||||
|
ElMessage.error('新密码不能与旧密码相同');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 发送修改密码请求
|
||||||
|
axios.put(BASE_URL + '/user/updatePassword', {
|
||||||
|
oldPassword: this.passwordForm.oldPassword,
|
||||||
|
newPassword: this.passwordForm.newPassword
|
||||||
|
}, { withCredentials: true })
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1) {
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
// 清空表单
|
||||||
|
this.passwordForm = {
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmPassword: ''
|
||||||
|
};
|
||||||
|
this.$router.push({name: 'Login'});
|
||||||
|
} else {
|
||||||
|
ElMessage.error('密码修改失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败:'+ error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateUsername() {
|
||||||
|
this.$refs.usernameForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
// 发送修改用户名请求
|
||||||
|
axios.put(BASE_URL + '/user/updateUsername', {
|
||||||
|
username: this.usernameForm.username
|
||||||
|
}, { withCredentials: true })
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1) {
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
// 更新当前用户信息
|
||||||
|
this.$emit('update:currentUser', this.usernameForm.username);
|
||||||
|
// this.currentUser.username = this.usernameForm.username;
|
||||||
|
// 清空表单
|
||||||
|
this.usernameForm = {
|
||||||
|
username: ''
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
ElMessage.error('用户名修改失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
});
|
||||||
|
} else ElMessage.error('请输入新用户名');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
259
src/components/RecycleBinPage.vue
Normal file
259
src/components/RecycleBinPage.vue
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
<template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="18">
|
||||||
|
<el-form ref="searchForm" :model="searchForm" >
|
||||||
|
<el-row>
|
||||||
|
<el-input v-model="searchForm.name" placeholder="请输入要查询的文件名" style="width: 240px; padding-right: 10px"></el-input>
|
||||||
|
<el-button type="primary" @click="getFiles" :icon="Search"></el-button>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<!-- 批量操作按钮 -->
|
||||||
|
<el-button-group>
|
||||||
|
<el-button type="primary" @click="handleBatchRestore">批量恢复</el-button>
|
||||||
|
<el-button type="danger" @click="handleBatchDelete">批量删除</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<!-- 文件列表 -->
|
||||||
|
<el-table
|
||||||
|
:data="files"
|
||||||
|
border
|
||||||
|
:v-loading="loading"
|
||||||
|
:default-sort="{ prop: 'name', order: 'ascending' }"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
:show-overflow-tooltip=true
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column label="序号" type="index" />
|
||||||
|
<el-table-column label="文件名" prop="name" sortable></el-table-column>
|
||||||
|
<el-table-column label="文件大小" prop="size" sortable>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{getFileSize(row.size)}}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="type" label="文件类型" sortable>
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
{{ getFileType(row.type) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最后更新时间" prop="updateTime" sortable></el-table-column>
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template v-slot="{ row }">
|
||||||
|
<el-button type="primary" size="small" @click="restoreFile(row)">恢复</el-button>
|
||||||
|
<el-button type="danger" size="small" @click="handleDelete(row)">彻底删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-pagination
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[5, 10, 20, 50]"
|
||||||
|
:total="total"
|
||||||
|
:background="true"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {ElMessage, ElMessageBox} from "element-plus";
|
||||||
|
import axios from "axios";
|
||||||
|
import {BASE_URL} from "@/main";
|
||||||
|
import {Search} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
Search() {
|
||||||
|
return Search
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
|
||||||
|
selectedRows: [], // 存储选中的行数据
|
||||||
|
searchForm: {name: ''},
|
||||||
|
files: [], // 所有文件数据
|
||||||
|
loading: false, // 加载状态
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 处理每页显示条数变化
|
||||||
|
handleSizeChange(size) {
|
||||||
|
this.pageSize = size;
|
||||||
|
this.currentPage = 1; // 重置当前页码为1
|
||||||
|
this.getFiles();
|
||||||
|
},
|
||||||
|
// 处理当前页码变化
|
||||||
|
handleCurrentChange(page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
this.getFiles();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 多选框选中状态变化时
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.selectedRows = selection; // 更新选中的行数据
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBatchRestore() {
|
||||||
|
let ids = [];
|
||||||
|
this.selectedRows.forEach(item => {
|
||||||
|
ids.push(item.id);
|
||||||
|
});
|
||||||
|
axios.put(BASE_URL+'/file'+'/0', ids, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1){
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
} else {
|
||||||
|
ElMessage.error('文件批量恢复失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
this.getFiles();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//console.log(error);
|
||||||
|
ElMessage.error('请求失败:'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBatchDelete() {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'确认要彻底删除这些文件吗?',
|
||||||
|
'Warning',
|
||||||
|
{
|
||||||
|
confirmButtonText: '是',
|
||||||
|
cancelButtonText: '否',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.batchPermanentDeleteFile();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDelete(file) {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
'确认要彻底删除该文件吗?',
|
||||||
|
'Warning',
|
||||||
|
{
|
||||||
|
confirmButtonText: '是',
|
||||||
|
cancelButtonText: '否',
|
||||||
|
type: 'warning',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
this.permanentDeleteFile(file);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取文件列表数据
|
||||||
|
getFiles() {
|
||||||
|
this.loading = true; // 显示加载状态
|
||||||
|
let url = BASE_URL + '/file/page/'+this.currentPage+'/'+this.pageSize+'/1';
|
||||||
|
axios.get(url, {params: {name: ''}, withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.code === 1) {
|
||||||
|
// this.files = response.data.data; // 设置文件列表数据
|
||||||
|
this.files = response.data.data.records; // 设置文件列表数据
|
||||||
|
this.total = response.data.data.total;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//console.error('文件列表获取失败', error);
|
||||||
|
ElMessage.error('文件列表获取失败'+error);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false; // 隐藏加载状态
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getFileSize(size) {
|
||||||
|
if(size/1024 < 1.0)
|
||||||
|
return size.toFixed(1)+'B';
|
||||||
|
if(size/(1024*1024) < 1.0)
|
||||||
|
return (size/(1024)).toFixed(1)+'KB';
|
||||||
|
if(size/(1024*1024*1024) < 1.0)
|
||||||
|
return (size/(1024*1024)).toFixed(1)+'MB';
|
||||||
|
if(size/(1024*1024*1024*1024) < 1.0)
|
||||||
|
return (size/(1024*1024*1024)).toFixed(1)+'GB';
|
||||||
|
},
|
||||||
|
getFileType(type) {
|
||||||
|
// 根据文件类型代码返回对应的文字描述
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
return '图片';
|
||||||
|
case 1:
|
||||||
|
return '视频';
|
||||||
|
case 2:
|
||||||
|
return '音频';
|
||||||
|
case 3:
|
||||||
|
return '文档';
|
||||||
|
default:
|
||||||
|
return '其他';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
restoreFile(file) {
|
||||||
|
// 恢复文件
|
||||||
|
//console.log('恢复文件:', file);
|
||||||
|
axios.put(BASE_URL+'/file/'+file.id+'/0', {}, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 0){
|
||||||
|
ElMessage.error('文件'+file.name+'恢复失败,'+response.data.msg);
|
||||||
|
} else {
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
}
|
||||||
|
this.getFiles();
|
||||||
|
}).catch(error => {
|
||||||
|
ElMessage.error("请求失败:"+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
permanentDeleteFile(file) {
|
||||||
|
// 删除文件的操作
|
||||||
|
//console.log('删除文件:', file);
|
||||||
|
axios.delete(BASE_URL+'/file/'+file.id, {withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1){
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
} else {
|
||||||
|
ElMessage.error('文件'+file.name+'删除失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
this.getFiles();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//console.log(error);
|
||||||
|
ElMessage.error('请求失败:'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
batchPermanentDeleteFile() {
|
||||||
|
let ids = [];
|
||||||
|
this.selectedRows.forEach(item => {
|
||||||
|
ids.push(item.id);
|
||||||
|
});
|
||||||
|
axios.delete(BASE_URL+'/file', {data: ids, withCredentials: true})
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.code === 1){
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
} else {
|
||||||
|
ElMessage.error('文件批量删除失败,'+response.data.msg);
|
||||||
|
}
|
||||||
|
this.getFiles();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
//console.log(error);
|
||||||
|
ElMessage.error('请求失败:'+error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
176
src/components/RegistryPage.vue
Normal file
176
src/components/RegistryPage.vue
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="background"></div>
|
||||||
|
<div class="content">
|
||||||
|
<h1 class="welcome-title">欢迎注册文件管理系统</h1>
|
||||||
|
<el-form :model="registerForm" :rules="rules" label-width="100px">
|
||||||
|
<el-form-item label="邮箱" prop="email">
|
||||||
|
<el-input v-model="registerForm.email" placeholder="请输入邮箱"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码" prop="password">
|
||||||
|
<el-input type="password" v-model="registerForm.password" placeholder="请输入密码"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="确认密码" prop="confirmPassword">
|
||||||
|
<el-input type="password" v-model="registerForm.confirmPassword" placeholder="请确认密码"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="验证码" prop="verificationCode">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="16">
|
||||||
|
<el-input v-model="registerForm.verificationCode" placeholder="请输入验证码"></el-input>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8" style="padding-left: 10px">
|
||||||
|
<el-button type="primary" @click="sendVerificationCode" :disabled="sendingVerificationCode">{{ sendButtonLabel }}</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="register">注册</el-button>
|
||||||
|
<el-button @click="goToLogin" plain>返回登录</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from "axios";
|
||||||
|
import { BASE_URL } from "@/main";
|
||||||
|
import { ElForm, ElFormItem, ElInput, ElButton, ElMessage, ElRow, ElCol } from 'element-plus';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ElForm,
|
||||||
|
ElFormItem,
|
||||||
|
ElInput,
|
||||||
|
ElButton,
|
||||||
|
ElRow,
|
||||||
|
ElCol
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
registerForm: {
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
verificationCode: ''
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||||
|
{type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur'}],
|
||||||
|
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||||
|
confirmPassword: [{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === this.registerForm.password) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
callback(new Error('两次输入的密码不一致'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
verificationCode: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
|
||||||
|
},
|
||||||
|
sendingVerificationCode: false,
|
||||||
|
sendButtonLabel: '获取验证码'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sendVerificationCode() {
|
||||||
|
if (!this.registerForm.email) {
|
||||||
|
ElMessage.error('请填写邮箱');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用正则表达式来验证邮箱格式是否正确
|
||||||
|
const emailPattern = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
|
||||||
|
if (!emailPattern.test(this.registerForm.email)) {
|
||||||
|
ElMessage.error('请输入有效的邮箱地址');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sendingVerificationCode) return;
|
||||||
|
|
||||||
|
this.sendingVerificationCode = true;
|
||||||
|
this.sendButtonLabel = '发送中...';
|
||||||
|
|
||||||
|
axios.post(BASE_URL + '/user/sendVerCode', {email: this.registerForm.email})
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.code === 1) {
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
//console.log('验证码发送成功', response.data);
|
||||||
|
} else {
|
||||||
|
ElMessage.error(response.data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,' + error);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.sendingVerificationCode = false;
|
||||||
|
this.sendButtonLabel = '获取验证码';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
register() {
|
||||||
|
if(!this.registerForm.email || !this.registerForm.password || !this.registerForm.confirmPassword || !this.registerForm.verificationCode){
|
||||||
|
ElMessage.error('请填写必填项');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this.registerForm.password !== this.registerForm.confirmPassword){
|
||||||
|
ElMessage.error('两次输入的密码不一致');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('注册', this.registerForm);
|
||||||
|
axios.post(BASE_URL + '/user/registry', this.registerForm)
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.code === 1) {
|
||||||
|
//console.log('注册成功', response.data);
|
||||||
|
ElMessage.success(response.data.data);
|
||||||
|
this.$router.push({name:'Login'});
|
||||||
|
} else if (response.data.code === 0) {
|
||||||
|
//console.log('注册失败', response.data);
|
||||||
|
ElMessage.error('注册失败,' + response.data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
ElMessage.error('请求失败,'+error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
goToLogin() {
|
||||||
|
//console.log('跳转到登录页面');
|
||||||
|
this.$router.push({name:'Login'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url('/public/background.png');
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 400px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
30
src/main.js
Normal file
30
src/main.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
import router from './router/router.js'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
export const BASE_URL = 'http://localhost:8080';
|
||||||
|
|
||||||
|
createApp(App).use(router).use(ElementPlus).mount('#app')
|
||||||
|
|
||||||
|
const debounce = (fn, delay) => {
|
||||||
|
let timer = null;
|
||||||
|
return function () {
|
||||||
|
let context = this;
|
||||||
|
let args = arguments;
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(function () {
|
||||||
|
fn.apply(context, args);
|
||||||
|
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _ResizeObserver = window.ResizeObserver;
|
||||||
|
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
|
||||||
|
constructor(callback) {
|
||||||
|
callback = debounce(callback, 16);
|
||||||
|
super(callback);
|
||||||
|
}
|
||||||
|
}
|
46
src/router/router.js
Normal file
46
src/router/router.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import LoginPage from '@/components/LoginPage.vue';
|
||||||
|
import RegistryPage from '@/components/RegistryPage.vue';
|
||||||
|
import HomePage from '@/components/HomePage.vue'
|
||||||
|
import {ElMessage} from "element-plus";
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Login',
|
||||||
|
component: LoginPage
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/registry',
|
||||||
|
name: 'Registry',
|
||||||
|
component: RegistryPage
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
|
name: 'Home',
|
||||||
|
component: HomePage,
|
||||||
|
meta: {requiresAuth: true}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes
|
||||||
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
//console.log(document.cookie);
|
||||||
|
const isAuthenticated = document.cookie.includes('user'); // 检查 cookie 中是否包含 user
|
||||||
|
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
ElMessage.error('尚未登录,请勿非法访问');
|
||||||
|
next({ path: '/' }); // 如果没有登录,则跳转到登录页面
|
||||||
|
} else {
|
||||||
|
next(); // 已登录,继续访问目标页面
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next(); // 不需要验证登录,直接访问
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
17
vue.config.js
Normal file
17
vue.config.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
module.exports = defineConfig({
|
||||||
|
transpileDependencies: true,
|
||||||
|
lintOnSave: false,
|
||||||
|
devServer: {
|
||||||
|
port: 8088,
|
||||||
|
client: {
|
||||||
|
overlay: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
chainWebpack: (config) => {
|
||||||
|
config.plugin('define').tap((definitions) => {
|
||||||
|
definitions[0].__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = false;
|
||||||
|
return definitions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
Loading…
x
Reference in New Issue
Block a user