240 lines
6.1 KiB
Markdown
240 lines
6.1 KiB
Markdown
# 文件管理页面设计文档
|
||
|
||
## 1. 页面概述
|
||
|
||
文件管理页面为系统管理员提供了一个集中管理所有上传文件的界面。这主要包括用户在提交反馈、任务报告等流程中上传的图片或其他附件。管理员能够在此页面上预览、搜索、筛选和删除文件,以维护系统存储的整洁和合规性。
|
||
|
||
## 2. 页面布局
|
||
|
||

|
||
|
||
### 2.1 布局结构
|
||
|
||
页面将采用现代化的卡片式画廊(Gallery)布局,以优化图片等视觉文件的预览体验。
|
||
- **顶部**: 操作和筛选区域。包含一个上传按钮(如果允许管理员直接上传)和按文件名、上传者或关联模块(如"反馈"、"任务")进行筛选的控件。
|
||
- **中部**: 文件画廊/列表。以卡片网格的形式展示文件,每个卡片包含文件缩略图、文件名、大小、上传时间等信息。
|
||
- **底部**: 分页控件。
|
||
|
||
## 3. 组件结构
|
||
|
||
```vue
|
||
<template>
|
||
<div class="file-management-page">
|
||
<el-page-header title="文件管理" content="系统上传资源概览" />
|
||
|
||
<el-card class="page-container">
|
||
<!-- 筛选与上传 -->
|
||
<div class="table-toolbar">
|
||
<el-form :model="filters" inline>
|
||
<el-form-item>
|
||
<el-input v-model="filters.filename" placeholder="按文件名搜索" clearable />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
<!-- <el-upload action="/api/files/upload" ... >
|
||
<el-button type="primary" icon="Upload">上传文件</el-button>
|
||
</el-upload> -->
|
||
</div>
|
||
|
||
<!-- 文件画廊 -->
|
||
<el-scrollbar v-loading="loading">
|
||
<div class="file-gallery">
|
||
<el-card v-for="file in files" :key="file.id" class="file-card" shadow="hover">
|
||
<el-image :src="file.url" fit="cover" class="file-thumbnail" :preview-src-list="[file.url]" />
|
||
<div class="file-info">
|
||
<span class="file-name" :title="file.originalName">{{ file.originalName }}</span>
|
||
<span class="file-size">{{ formatFileSize(file.size) }}</span>
|
||
<el-popconfirm title="确定要删除此文件吗?" @confirm="handleDelete(file)">
|
||
<template #reference>
|
||
<el-button link type="danger" icon="Delete" class="delete-btn" />
|
||
</template>
|
||
</el-popconfirm>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</el-scrollbar>
|
||
|
||
<!-- 分页 -->
|
||
<el-pagination
|
||
v-if="total > 0"
|
||
class="pagination-container"
|
||
:current-page="pagination.page"
|
||
:page-size="pagination.pageSize"
|
||
:total="total"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
/>
|
||
</el-card>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
## 4. 数据结构
|
||
|
||
```typescript
|
||
// 文件筛选条件
|
||
interface FileFilters {
|
||
filename?: string;
|
||
}
|
||
|
||
// 文件数据
|
||
interface FileItem {
|
||
id: number;
|
||
url: string; // 文件的访问 URL
|
||
originalName: string; // 原始文件名
|
||
size: number; // 文件大小 (bytes)
|
||
mimeType: string;
|
||
uploadedAt: string;
|
||
}
|
||
```
|
||
|
||
## 5. 状态管理
|
||
|
||
```typescript
|
||
// 组件内状态
|
||
const filters = ref<FileFilters>({});
|
||
const pagination = ref({ page: 1, pageSize: 20 });
|
||
|
||
// 全局状态 (Pinia Store)
|
||
const fileStore = useFileStore();
|
||
const { files, total, loading } = storeToRefs(fileStore);
|
||
```
|
||
|
||
## 6. 交互逻辑
|
||
|
||
### 6.1 数据获取
|
||
|
||
```typescript
|
||
const fetchFiles = () => {
|
||
fileStore.fetchFiles({ ...filters.value, ...pagination.value });
|
||
};
|
||
|
||
onMounted(fetchFiles);
|
||
|
||
const handleSearch = () => {
|
||
pagination.value.page = 1;
|
||
fetchFiles();
|
||
};
|
||
// ... 分页逻辑
|
||
```
|
||
|
||
### 6.2 文件操作
|
||
|
||
```typescript
|
||
const handleDelete = async (file: FileItem) => {
|
||
await fileStore.deleteFile(file.id);
|
||
ElMessage.success('文件删除成功');
|
||
fetchFiles(); // 刷新列表
|
||
};
|
||
```
|
||
|
||
### 6.3 辅助函数
|
||
|
||
```typescript
|
||
const formatFileSize = (bytes: number): string => {
|
||
if (bytes === 0) return '0 B';
|
||
const k = 1024;
|
||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||
};
|
||
```
|
||
|
||
## 7. API 调用
|
||
|
||
```typescript
|
||
// api/files.ts
|
||
export const fileApi = {
|
||
getFiles: (params) => apiClient.get('/files', { params }),
|
||
deleteFile: (id: number) => apiClient.delete(`/files/${id}`),
|
||
};
|
||
|
||
// stores/file.ts
|
||
export const useFileStore = defineStore('file', {
|
||
state: () => ({
|
||
files: [] as FileItem[],
|
||
total: 0,
|
||
loading: false,
|
||
}),
|
||
actions: {
|
||
async fetchFiles(params) {
|
||
this.loading = true;
|
||
try {
|
||
const { data } = await fileApi.getFiles(params);
|
||
this.files = data.items;
|
||
this.total = data.total;
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
async deleteFile(id: number) {
|
||
await fileApi.deleteFile(id);
|
||
}
|
||
}
|
||
});
|
||
```
|
||
|
||
## 8. 样式设计
|
||
|
||
```scss
|
||
.file-management-page {
|
||
.page-container {
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.file-gallery {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.file-card {
|
||
.file-thumbnail {
|
||
width: 100%;
|
||
height: 150px;
|
||
display: block;
|
||
}
|
||
|
||
.file-info {
|
||
padding: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
|
||
.file-name {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
flex-grow: 1;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.file-size {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.delete-btn {
|
||
margin-left: 10px;
|
||
padding: 2px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.pagination-container {
|
||
margin-top: 24px;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 9. 测试用例
|
||
|
||
- **集成测试**:
|
||
- 测试页面是否能正确加载并以卡片形式展示文件列表。
|
||
- 测试搜索功能是否能正确过滤文件。
|
||
- 测试分页功能。
|
||
- 测试点击图片是否能触发大图预览。
|
||
- 测试删除文件功能,并验证文件是否从列表中移除。 |