重构云文件管理系统

This commit is contained in:
2026-04-02 23:35:48 +08:00
parent ceb6c8258c
commit 38bcbd6ed2
9 changed files with 48 additions and 51 deletions

View File

@@ -1,7 +1,7 @@
-- 创建数据库 -- 创建数据库
CREATE DATABASE IF NOT EXISTS file_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE file_system; USE system;
-- 用户表 -- 用户表
CREATE TABLE IF NOT EXISTS sys_user ( CREATE TABLE IF NOT EXISTS sys_user (
@@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS sys_user (
phone VARCHAR(20) COMMENT '手机号', phone VARCHAR(20) COMMENT '手机号',
status INT DEFAULT 1 COMMENT '状态 0-禁用 1-启用', status INT DEFAULT 1 COMMENT '状态 0-禁用 1-启用',
storage_used BIGINT DEFAULT 0 COMMENT '已用存储空间(字节)', storage_used BIGINT DEFAULT 0 COMMENT '已用存储空间(字节)',
storage_limit BIGINT DEFAULT 10737418240 COMMENT '存储限制(字节) 默认10GB', storage_limit BIGINT DEFAULT 21474836480 COMMENT '存储限制(字节) 默认20GB',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
is_deleted INT DEFAULT 0 COMMENT '是否删除 0-否 1-是' is_deleted INT DEFAULT 0 COMMENT '是否删除 0-否 1-是'
@@ -73,6 +73,7 @@ CREATE TABLE IF NOT EXISTS sys_message (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息表';
-- 插入默认管理员账户 (密码: admin123) -- 插入默认管理员账户 (密码: admin123)
-- BCrypt hash generated with strength 10
INSERT INTO sys_user (username, password, nickname, status, storage_limit) INSERT INTO sys_user (username, password, nickname, status, storage_limit)
VALUES ('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', '管理员', 1, 10737418240) VALUES ('admin', '$2a$10$EqKcp1WFKVQISheBxmXNGexPR.i7QYXOJC.OFfQDT8iSaHuuPdlrW', '管理员', 1, 21474836480)
ON DUPLICATE KEY UPDATE username = username; ON DUPLICATE KEY UPDATE password = '$2a$10$EqKcp1WFKVQISheBxmXNGexPR.i7QYXOJC.OFfQDT8iSaHuuPdlrW';

View File

@@ -886,7 +886,6 @@
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/lodash": "*" "@types/lodash": "*"
} }
@@ -1422,15 +1421,13 @@
"version": "4.17.23", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/lodash-es": { "node_modules/lodash-es": {
"version": "4.17.23", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/lodash-unified": { "node_modules/lodash-unified": {
"version": "1.0.3", "version": "1.0.3",
@@ -1637,7 +1634,6 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.43", "postcss": "^8.4.43",
@@ -1697,7 +1693,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz",
"integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==", "integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.31", "@vue/compiler-dom": "3.5.31",
"@vue/compiler-sfc": "3.5.31", "@vue/compiler-sfc": "3.5.31",

View File

@@ -1,4 +1,4 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
title="消息" title="消息"
@@ -310,8 +310,8 @@ const getFileColor = (name) => {
const loadContacts = async () => { const loadContacts = async () => {
try { try {
const res = await getUsers() const data = await getUsers()
contacts.value = (res.data || []).map((u, i) => ({ contacts.value = (data || []).map((u, i) => ({
...u, ...u,
id: u.id, id: u.id,
name: u.nickname || u.username, name: u.nickname || u.username,
@@ -327,8 +327,8 @@ const loadContacts = async () => {
const loadUnreadChats = async () => { const loadUnreadChats = async () => {
try { try {
const res = await getUnreadList() const data = await getUnreadList()
const list = res.data || [] const list = data || []
if (list.length === 0) return if (list.length === 0) return
// 加载联系人后再填充未读数据 // 加载联系人后再填充未读数据
list.forEach(item => { list.forEach(item => {
@@ -372,8 +372,8 @@ const selectContact = async (contact) => {
contact.unread = 0 contact.unread = 0
loadingMessages.value = true loadingMessages.value = true
try { try {
const res = await getMessages({ userId: contact.id }) const data = await getMessages({ userId: contact.id })
messages.value[contact.id] = (res.data || []).map(msg => { messages.value[contact.id] = (data || []).map(msg => {
const isSelf = String(msg.fromUserId) === String(userStore.userId) const isSelf = String(msg.fromUserId) === String(userStore.userId)
return { return {
...msg, ...msg,
@@ -474,11 +474,11 @@ const handleImageSelect = async (e) => {
try { try {
const formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
const res = await uploadChatFile(formData) const data = await uploadChatFile(formData)
chatService.send({ chatService.send({
type: 'chat', type: 'chat',
toUserId: currentContact.value.id, toUserId: currentContact.value.id,
content: res.url, content: data.url,
msgType: 'image' msgType: 'image'
}) })
updateRecentChat(currentContact.value, '[图片]') updateRecentChat(currentContact.value, '[图片]')
@@ -525,11 +525,11 @@ const handleFileSelect = async (e) => {
try { try {
const formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
const res = await uploadChatFile(formData) const data = await uploadChatFile(formData)
chatService.send({ chatService.send({
type: 'chat', type: 'chat',
toUserId: currentContact.value.id, toUserId: currentContact.value.id,
content: res.url, content: data.url,
msgType: 'file', msgType: 'file',
fileName: file.name, fileName: file.name,
fileSize: file.size fileSize: file.size

View File

@@ -1,4 +1,4 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
title="个人信息" title="个人信息"
@@ -136,10 +136,10 @@ const handleAvatarChange = async (e) => {
try { try {
const formData = new FormData() const formData = new FormData()
formData.append('avatar', file) formData.append('avatar', file)
const res = await request.post('/users/avatar', formData, { const data = await request.post('/users/avatar', formData, {
headers: { 'Content-Type': 'multipart/form-data' } headers: { 'Content-Type': 'multipart/form-data' }
}) })
const newUrl = res.data?.url || '' const newUrl = data?.url || ''
avatarUrl.value = newUrl avatarUrl.value = newUrl
userStore.setUser({ avatar: newUrl }) userStore.setUser({ avatar: newUrl })
ElMessage.success('头像更新成功') ElMessage.success('头像更新成功')

View File

@@ -1,4 +1,4 @@
<template> <template>
<el-dialog <el-dialog
v-model="visible" v-model="visible"
title="共享文件" title="共享文件"
@@ -65,8 +65,8 @@ const userList = ref([])
const loadUsers = async () => { const loadUsers = async () => {
try { try {
const res = await getUsers() const data = await getUsers()
userList.value = res.data || [] userList.value = data || []
} catch (e) { } catch (e) {
ElMessage.error('获取用户列表失败') ElMessage.error('获取用户列表失败')
} }

View File

@@ -1,4 +1,4 @@
<template> <template>
<div class="top-navbar"> <div class="top-navbar">
<div class="navbar-left"> <div class="navbar-left">
<div class="logo-icon"> <div class="logo-icon">
@@ -145,8 +145,8 @@ let pollTimer = null
let wsUnsubscribe = null let wsUnsubscribe = null
const checkUnread = () => { const checkUnread = () => {
getUnreadCount().then(res => { getUnreadCount().then(data => {
totalUnread.value = res.data.count || 0 totalUnread.value = data.count || 0
}).catch(() => {}) }).catch(() => {})
} }

View File

@@ -251,8 +251,7 @@ const remainingStorage = computed(() => {
// 刷新存储数据(从后端精确重算) // 刷新存储数据(从后端精确重算)
const refreshStorage = async () => { const refreshStorage = async () => {
try { try {
const res = await getCurrentUser() const data = await getCurrentUser()
const data = res.data
if (data) { if (data) {
userStore.setUser({ userStore.setUser({
storageUsed: data.storageUsed ?? 0, storageUsed: data.storageUsed ?? 0,
@@ -299,26 +298,26 @@ const loadFiles = async () => {
loading.value = true loading.value = true
currentPage.value = 1 currentPage.value = 1
try { try {
let res let data
switch (activeMenu.value) { switch (activeMenu.value) {
case 'trash': res = await getTrashFiles(currentFolderId.value ? { folderId: currentFolderId.value } : {}); break case 'trash': data = await getTrashFiles(currentFolderId.value ? { folderId: currentFolderId.value } : {}); break
case 'my-share': case 'my-share':
if (currentFolderId.value) { if (currentFolderId.value) {
res = await getSharedByMeFolderFiles(currentFolderId.value) data = await getSharedByMeFolderFiles(currentFolderId.value)
} else { } else {
res = await getSharedByMe() data = await getSharedByMe()
} }
break break
case 'shared-to-me': case 'shared-to-me':
if (currentFolderId.value) { if (currentFolderId.value) {
res = await getSharedFolderFiles(currentFolderId.value) data = await getSharedFolderFiles(currentFolderId.value)
} else { } else {
res = await getSharedToMe() data = await getSharedToMe()
} }
break break
default: res = await getFiles({ folderId: currentFolderId.value, keyword: searchKeyword.value }) default: data = await getFiles({ folderId: currentFolderId.value, keyword: searchKeyword.value })
} }
files.value = res.data || [] files.value = data || []
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} finally { } finally {
@@ -601,8 +600,8 @@ const handleBatchMove = async () => {
try { try {
const selectedIds = selectedFiles.value.map(f => f.id) const selectedIds = selectedFiles.value.map(f => f.id)
const res = await getMovableFolders(selectedIds, currentFolderId.value) const data = await getMovableFolders(selectedIds, currentFolderId.value)
movableFolders.value = res.data || [] movableFolders.value = data || []
batchMoveVisible.value = true batchMoveVisible.value = true
} catch (e) { } catch (e) {
ElMessage.error('获取目录列表失败') ElMessage.error('获取目录列表失败')

View File

@@ -1,4 +1,4 @@
<template> <template>
<el-form :model="form" :rules="rules" ref="formRef" @submit.prevent="handleLogin"> <el-form :model="form" :rules="rules" ref="formRef" @submit.prevent="handleLogin">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
@@ -57,11 +57,13 @@ const handleLogin = async () => {
loading.value = true loading.value = true
try { try {
const res = await login(form) // login 返回的是 res.data经过 request.js 拦截器处理)
// res.data = { token, user } // 后端 ApiResult 格式: { code, message, data }
emit('success', res.data) // data = { token, user }
const data = await login(form)
emit('success', data)
} catch (e) { } catch (e) {
ElMessage.error(e.response?.data?.message || '账号或密码错误') ElMessage.error(e.message || '账号或密码错误')
} finally { } finally {
loading.value = false loading.value = false
} }

View File

@@ -1,4 +1,4 @@
<template> <template>
<el-form :model="form" :rules="rules" ref="formRef" @submit.prevent="handleRegister"> <el-form :model="form" :rules="rules" ref="formRef" @submit.prevent="handleRegister">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
@@ -107,7 +107,7 @@ const handleRegister = async () => {
ElMessage.success('注册成功,请登录') ElMessage.success('注册成功,请登录')
emit('success') emit('success')
} catch (e) { } catch (e) {
ElMessage.error(e.response?.data?.message || '注册失败,请重试') ElMessage.error(e.message || '注册失败,请重试')
} finally { } finally {
loading.value = false loading.value = false
} }