系统管理基本搞定

This commit is contained in:
李志强 2026-04-01 15:51:34 +08:00
parent e927026495
commit f627414c36

View File

@ -4,7 +4,9 @@
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span class="title"> <span class="title">
<el-icon><Folder /></el-icon> <el-icon>
<Folder />
</el-icon>
文件管理系统 文件管理系统
</span> </span>
<el-button :icon="Refresh" @click="refushData"> 刷新 </el-button> <el-button :icon="Refresh" @click="refushData"> 刷新 </el-button>
@ -18,40 +20,29 @@
<template #header> <template #header>
<div class="card-title"> <div class="card-title">
<div style="display: flex; align-items: center; gap: 8px"> <div style="display: flex; align-items: center; gap: 8px">
<el-icon><Grid /></el-icon> <el-icon>
<Grid />
</el-icon>
<span>文件分组</span> <span>文件分组</span>
</div> </div>
<el-button <el-button type="primary" size="small" @click="handleCreateCategory">新建分组</el-button>
type="primary"
size="small"
@click="handleCreateCategory"
>新建分组</el-button
>
</div> </div>
</template> </template>
<el-input <el-input v-model="groupSearchQuery" placeholder="搜索分组..." :prefix-icon="Search" clearable
v-model="groupSearchQuery" class="search-input" />
placeholder="搜索分组..."
:prefix-icon="Search"
clearable
class="search-input"
/>
<el-scrollbar class="group-list" v-loading="loading"> <el-scrollbar class="group-list" v-loading="loading">
<div <div v-for="group in filteredGroups" :key="group.id" :class="[
v-for="group in filteredGroups" 'group-item',
:key="group.id" { active: selectedGroup?.id === group.id },
:class="[ ]" @click="selectGroup(group)">
'group-item',
{ active: selectedGroup?.id === group.id },
]"
@click="selectGroup(group)"
>
<div class="group-info"> <div class="group-info">
<div class="group-name"> <div class="group-name">
<div style="display: flex; align-items: center; gap: 8px"> <div style="display: flex; align-items: center; gap: 8px">
<el-icon class="group-icon"><FolderOpened /></el-icon> <el-icon class="group-icon">
<FolderOpened />
</el-icon>
{{ group.name }} {{ group.name }}
</div> </div>
</div> </div>
@ -59,47 +50,35 @@
</div> </div>
<div class="group-actions" v-if="group.id !== 0"> <div class="group-actions" v-if="group.id !== 0">
<div class="action-buttons" @click.stop> <div class="action-buttons" @click.stop>
<el-button <el-button type="primary" link size="small" :icon="Edit" @click="handleRenameCategory(group)"
type="primary" title="重命名" />
link <el-button type="danger" link size="small" :icon="Delete" @click="deleteFileCateData(group.id)"
size="small" title="删除" />
:icon="Edit"
@click="handleRenameCategory(group)"
title="重命名"
/>
<el-button
type="danger"
link
size="small"
:icon="Delete"
@click="deleteFileCateData(group.id)"
title="删除"
/>
</div> </div>
</div> </div>
</div> </div>
<el-empty <el-empty v-if="filteredGroups.length === 0" description="暂无分组数据" />
v-if="filteredGroups.length === 0"
description="暂无分组数据"
/>
</el-scrollbar> </el-scrollbar>
</el-card> </el-card>
</el-col> </el-col>
<!-- 右侧文件内容 --> <!-- 右侧文件内容 -->
<el-col :span="18"> <el-col :span="18">
<el-card <el-card style="min-height: 600px" shadow="hover" class="file-content-card">
style="min-height: 600px"
shadow="hover"
class="file-content-card"
>
<template #header> <template #header>
<div class="card-title"> <div class="card-title">
<div style="display: flex; align-items: center; gap: 8px"> <div style="display: flex; align-items: center; gap: 8px">
<el-icon><Document /></el-icon> <el-icon>
<Document />
</el-icon>
<span>文件列表</span> <span>文件列表</span>
</div> </div>
<el-button type="primary" :icon="Upload" @click="handleUpload">
上传文件
</el-button>
</div> </div>
</template> </template>
@ -110,56 +89,35 @@
<div v-else class="file-content"> <div v-else class="file-content">
<!-- 工具栏 --> <!-- 工具栏 -->
<div class="toolbar"> <div class="toolbar">
<el-input <el-input v-model="fileSearchQuery" placeholder="搜索文件..." :prefix-icon="Search" clearable
v-model="fileSearchQuery" style="width: 300px" @clear="handleFileSearch" @keyup.enter="handleFileSearch" />
placeholder="搜索文件..."
:prefix-icon="Search"
clearable
style="width: 300px"
@clear="handleFileSearch"
@keyup.enter="handleFileSearch"
/>
<div class="actions"> <div class="actions">
<el-button <el-link type="warning" :underline="false" @click="handleBatchMove"
type="primary" :disabled="selectedFileIds.length === 0">
:icon="Upload" <el-icon :size="16">
@click="handleUpload" <FolderOpened />
> </el-icon>
上传文件
</el-button>
<el-button
type="warning"
:icon="FolderOpened"
@click="handleBatchMove"
:disabled="selectedFileIds.length === 0"
>
批量移动 ({{ selectedFileIds.length }}) 批量移动 ({{ selectedFileIds.length }})
</el-button> </el-link>
<el-button <el-link type="danger" :underline="false" @click="handleBatchDelete"
type="danger" :disabled="selectedFileIds.length === 0">
:icon="Delete" <el-icon :size="16">
@click="handleBatchDelete" <Delete />
:disabled="selectedFileIds.length === 0" </el-icon>
>
批量删除 ({{ selectedFileIds.length }}) 批量删除 ({{ selectedFileIds.length }})
</el-button> </el-link>
<el-button <el-link type="danger" :underline="false" @click="handleBatchDeletePermanently"
type="danger" :disabled="selectedFileIds.length === 0">
plain <el-icon :size="16">
@click="handleBatchDeletePermanently" <DeleteFilled />
:disabled="selectedFileIds.length === 0" </el-icon>
>
<el-icon><DeleteFilled /></el-icon>
批量永久删除 ({{ selectedFileIds.length }}) 批量永久删除 ({{ selectedFileIds.length }})
</el-button> </el-link>
<el-button <el-link type="info" :underline="false" @click="clearSelection"
type="info" :disabled="selectedFileIds.length === 0">
@click="clearSelection"
:disabled="selectedFileIds.length === 0"
>
取消选择 取消选择
</el-button> </el-link>
</div> </div>
</div> </div>
@ -168,28 +126,16 @@
<div class="file-grid-header" v-if="files.length > 0"> <div class="file-grid-header" v-if="files.length > 0">
<button class="allbtns" @click="toggleSelectAll">全选/反选</button> <button class="allbtns" @click="toggleSelectAll">全选/反选</button>
</div> </div>
<div <div v-loading="loading" class="file-grid" v-if="files.length > 0">
v-loading="loading" <el-card v-for="file in files" :key="file.id" shadow="hover"
class="file-grid"
v-if="files.length > 0"
>
<el-card
v-for="file in files"
:key="file.id"
shadow="hover"
:class="['file-card', { 'file-card-selected': selectedFileIds.includes(file.id) }]" :class="['file-card', { 'file-card-selected': selectedFileIds.includes(file.id) }]"
@click="toggleFileSelection(file)" @click="toggleFileSelection(file)">
>
<div class="file-checkbox"> <div class="file-checkbox">
<el-checkbox :model-value="selectedFileIds.includes(file.id)" /> <el-checkbox :model-value="selectedFileIds.includes(file.id)" />
</div> </div>
<div class="file-icon"> <div class="file-icon">
<div v-if="isImage(file)"> <div v-if="isImage(file)">
<img <img :src="getFileUrl(file.url)" alt="file" class="preview-image" />
:src="getFileUrl(file.url)"
alt="file"
class="preview-image"
/>
</div> </div>
<div v-else-if="isVideo(file)"> <div v-else-if="isVideo(file)">
<video :src="getFileUrl(file.url)" alt="file" /> <video :src="getFileUrl(file.url)" alt="file" />
@ -214,67 +160,33 @@
}}</span> }}</span>
</div> </div>
<div class="file-actions"> <div class="file-actions">
<el-button <el-button v-if="isImage(file)" type="primary" link @click.stop="handleImagePreview(file)"
v-if="isImage(file)" title="预览">
type="primary"
link
@click.stop="handleImagePreview(file)"
title="预览"
>
<i class="fa-solid fa-eye"></i> <i class="fa-solid fa-eye"></i>
</el-button> </el-button>
<el-button <el-button type="primary" link @click.stop="handleMoveClick(file)" title="移动">
type="primary"
link
@click.stop="handleMoveClick(file)"
title="移动"
>
<i class="fa-solid fa-arrows-up-down-left-right"></i> <i class="fa-solid fa-arrows-up-down-left-right"></i>
</el-button> </el-button>
<el-button <el-button type="primary" link @click.stop="handleDownload(file)" title="下载">
type="primary"
link
@click.stop="handleDownload(file)"
title="下载"
>
<i class="fa-solid fa-download"></i> <i class="fa-solid fa-download"></i>
</el-button> </el-button>
<el-button <el-button type="danger" link @click.stop="handleDelete(file)" title="删除">
type="danger"
link
@click.stop="handleDelete(file)"
title="删除"
>
<i class="fa-regular fa-trash-can"></i> <i class="fa-regular fa-trash-can"></i>
</el-button> </el-button>
<el-button <el-button type="danger" link @click.stop="handlePermanentDelete(file)" title="彻底删除">
type="danger"
link
@click.stop="handlePermanentDelete(file)"
title="彻底删除"
>
<i class="fa-solid fa-trash"></i> <i class="fa-solid fa-trash"></i>
</el-button> </el-button>
</div> </div>
</el-card> </el-card>
</div> </div>
<el-empty <el-empty v-if="!loading && files.length === 0" description="该分组暂无文件" />
v-if="!loading && files.length === 0"
description="该分组暂无文件"
/>
</div> </div>
<!-- 分页组件 --> <!-- 分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize"
v-model:current-page="currentPage" :page-sizes="[12, 24, 48, 96]" :total="total" layout="total, sizes, prev, pager, next, jumper"
v-model:page-size="pageSize" @size-change="handleSizeChange" @current-change="handlePageChange" />
:page-sizes="[12, 24, 48, 96]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -283,114 +195,46 @@
</el-card> </el-card>
<!-- 重命名分组对话框 --> <!-- 重命名分组对话框 -->
<el-dialog <el-dialog v-model="showRenameDialog" title="重命名分组" width="400px" @close="handleRenameDialogClose">
v-model="showRenameDialog" <el-form ref="renameFormRef" :model="renameForm" :rules="renameFormRules" label-width="80px">
title="重命名分组"
width="400px"
@close="handleRenameDialogClose"
>
<el-form
ref="renameFormRef"
:model="renameForm"
:rules="renameFormRules"
label-width="80px"
>
<el-form-item label="分组名称" prop="name"> <el-form-item label="分组名称" prop="name">
<el-input <el-input v-model="renameForm.name" placeholder="请输入分组名称" maxlength="20" show-word-limit />
v-model="renameForm.name"
placeholder="请输入分组名称"
maxlength="20"
show-word-limit
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="showRenameDialog = false">取消</el-button> <el-button @click="showRenameDialog = false">取消</el-button>
<el-button <el-button type="primary" @click="handleRenameConfirm" :loading="renaming">
type="primary"
@click="handleRenameConfirm"
:loading="renaming"
>
确定 确定
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<!-- 上传文件对话框 --> <!-- 上传文件对话框 -->
<UploadFile <UploadFile v-model="showUploadDialog" :category-id="selectedGroup?.id" @success="handleUploadSuccess" />
v-model="showUploadDialog"
:category-id="selectedGroup?.id"
@success="handleUploadSuccess"
/>
<!-- 图片预览对话框 --> <!-- 图片预览对话框 -->
<el-dialog <el-dialog v-model="showImagePreview" title="图片预览" width="90%" :before-close="handlePreviewClose"
v-model="showImagePreview" class="image-preview-dialog" @opened="handlePreviewOpened">
title="图片预览"
width="90%"
:before-close="handlePreviewClose"
class="image-preview-dialog"
@opened="handlePreviewOpened"
>
<template #header> <template #header>
<div class="preview-header"> <div class="preview-header">
<span>图片预览</span> <span>图片预览</span>
<div class="preview-toolbar"> <div class="preview-toolbar">
<el-button <el-button type="primary" :icon="ZoomOut" circle size="small" @click="zoomOut"
type="primary" :disabled="imageScale <= 0.5" />
:icon="ZoomOut"
circle
size="small"
@click="zoomOut"
:disabled="imageScale <= 0.5"
/>
<span class="zoom-text">{{ Math.round(imageScale * 100) }}%</span> <span class="zoom-text">{{ Math.round(imageScale * 100) }}%</span>
<el-button <el-button type="primary" :icon="ZoomIn" circle size="small" @click="zoomIn" :disabled="imageScale >= 3" />
type="primary" <el-button type="primary" :icon="RefreshLeft" circle size="small" @click="resetZoom" />
:icon="ZoomIn" <el-button type="primary" :icon="FullScreen" circle size="small" @click="toggleFullscreen" />
circle
size="small"
@click="zoomIn"
:disabled="imageScale >= 3"
/>
<el-button
type="primary"
:icon="RefreshLeft"
circle
size="small"
@click="resetZoom"
/>
<el-button
type="primary"
:icon="FullScreen"
circle
size="small"
@click="toggleFullscreen"
/>
</div> </div>
</div> </div>
</template> </template>
<div <div class="preview-container" ref="previewContainerRef" @wheel="handleWheel">
class="preview-container" <div class="preview-image-wrapper" :style="{
ref="previewContainerRef" transform: `scale(${imageScale}) translate(${imagePosition.x}px, ${imagePosition.y}px)`,
@wheel="handleWheel" transformOrigin: 'center center',
> cursor: imageScale > 1 ? 'move' : 'default',
<div }" @mousedown="handleMouseDown">
class="preview-image-wrapper" <img :src="previewImageUrl" alt="预览图片" class="preview-image-full" draggable="false" @load="handleImageLoad" />
:style="{
transform: `scale(${imageScale}) translate(${imagePosition.x}px, ${imagePosition.y}px)`,
transformOrigin: 'center center',
cursor: imageScale > 1 ? 'move' : 'default',
}"
@mousedown="handleMouseDown"
>
<img
:src="previewImageUrl"
alt="预览图片"
class="preview-image-full"
draggable="false"
@load="handleImageLoad"
/>
</div> </div>
<div class="preview-info" v-if="previewFile"> <div class="preview-info" v-if="previewFile">
<div class="preview-name">{{ previewFile.name }}</div> <div class="preview-name">{{ previewFile.name }}</div>
@ -400,48 +244,25 @@
</el-dialog> </el-dialog>
<!-- 移动文件对话框 --> <!-- 移动文件对话框 -->
<MoveFile <MoveFile v-model="showMoveDialog" :file-id="selectedFile?.id" :current-cate-id="selectedFile?.groupId"
v-model="showMoveDialog" :cate-list="groups" @moved="handleMoveSuccess" />
:file-id="selectedFile?.id"
:current-cate-id="selectedFile?.groupId"
:cate-list="groups"
@moved="handleMoveSuccess"
/>
<!-- 重命名文件分组对话框 --> <!-- 重命名文件分组对话框 -->
<RenameCategory <RenameCategory ref="renameCategoryRef" v-model="showRenameCategoryDialog" :category-id="currentRenameCategoryId"
ref="renameCategoryRef" :category-name="currentRenameCategoryName" @success="handleRenameCategorySuccess"
v-model="showRenameCategoryDialog" @close="handleRenameCategoryClose" />
:category-id="currentRenameCategoryId"
:category-name="currentRenameCategoryName"
@success="handleRenameCategorySuccess"
@close="handleRenameCategoryClose"
/>
<!-- 创建文件分组对话框 --> <!-- 创建文件分组对话框 -->
<CreateCategory <CreateCategory ref="createCategoryRef" v-model="showCreateCategoryDialog" @success="handleCreateCategorySuccess"
ref="createCategoryRef" @close="handleCreateCategoryClose" />
v-model="showCreateCategoryDialog"
@success="handleCreateCategorySuccess"
@close="handleCreateCategoryClose"
/>
<!-- 批量移动文件对话框 --> <!-- 批量移动文件对话框 -->
<el-dialog <el-dialog v-model="showBatchMoveDialog" title="批量移动文件" width="400px">
v-model="showBatchMoveDialog"
title="批量移动文件"
width="400px"
>
<el-form label-width="80px"> <el-form label-width="80px">
<el-form-item label="目标分组"> <el-form-item label="目标分组">
<el-select v-model="batchMoveTargetCate" placeholder="请选择目标分组"> <el-select v-model="batchMoveTargetCate" placeholder="请选择目标分组">
<el-option :value="0" label="未分类" /> <el-option :value="0" label="未分类" />
<el-option <el-option v-for="group in groups" :key="group.id" :value="group.id" :label="group.name" />
v-for="group in groups"
:key="group.id"
:value="group.id"
:label="group.name"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -1375,11 +1196,11 @@ onMounted(() => {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
.allbtns{ .allbtns {
color:#0081ff; color: #0081ff;
&:hover{ &:hover {
color:#0088ff; color: #0088ff;
} }
} }
} }