mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-13 02:57:11 +08:00
feat: 文件管理呈目录形式展示 (#60)
This commit is contained in:
@@ -24,3 +24,13 @@ export function deleteFile(id: string) {
|
|||||||
export function getFileStatistics() {
|
export function getFileStatistics() {
|
||||||
return http.get<T.FileStatisticsResp>(`${BASE_URL}/statistics`)
|
return http.get<T.FileStatisticsResp>(`${BASE_URL}/statistics`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @desc 根据sha256检测文件是否已经在服务器存在 */
|
||||||
|
export function checkFile(sha256: string) {
|
||||||
|
return http.get<T.FileItem>(`${BASE_URL}/check`, { fileHash: sha256 })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 创建文件夹 */
|
||||||
|
export function createDir(path: string, name: string) {
|
||||||
|
return http.post<T.FileItem>(`${BASE_URL}/createDir`, { parentPath: path, name })
|
||||||
|
}
|
||||||
|
@@ -207,7 +207,7 @@ export interface FileItem {
|
|||||||
parentPath: string
|
parentPath: string
|
||||||
absPath: string
|
absPath: string
|
||||||
metadata: string
|
metadata: string
|
||||||
md5: string
|
sha256: string
|
||||||
contentType: string
|
contentType: string
|
||||||
thumbnailSize: number
|
thumbnailSize: number
|
||||||
thumbnailUrl: string
|
thumbnailUrl: string
|
||||||
|
@@ -46,3 +46,5 @@ export const OfficeTypes = ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx', 'pdf']
|
|||||||
export const WordTypes = ['doc', 'docx']
|
export const WordTypes = ['doc', 'docx']
|
||||||
|
|
||||||
export const ExcelTypes = ['xls', 'xlsx']
|
export const ExcelTypes = ['xls', 'xlsx']
|
||||||
|
|
||||||
|
export const DirTypes = ['dir']
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="大小">{{ formatFileSize(data.size) }}</a-descriptions-item>
|
<a-descriptions-item label="大小">{{ formatFileSize(data.size) }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="路径">{{ data.absPath + getFileName(data) }}</a-descriptions-item>
|
<a-descriptions-item label="路径">{{ data.absPath + getFileName(data) }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="MD5">{{ data.md5 }}</a-descriptions-item>
|
<a-descriptions-item label="SHA256">{{ data.sha256 }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="上传时间">{{ data.createTime }}</a-descriptions-item>
|
<a-descriptions-item label="上传时间">{{ data.createTime }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="修改时间">{{ data.updateTime }}</a-descriptions-item>
|
<a-descriptions-item label="修改时间">{{ data.updateTime }}</a-descriptions-item>
|
||||||
<a-descriptions-item label="存储名称">{{ data.storageName }}</a-descriptions-item>
|
<a-descriptions-item label="存储名称">{{ data.storageName }}</a-descriptions-item>
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
scroll-to-close
|
scroll-to-close
|
||||||
>
|
>
|
||||||
<a-grid-item>
|
<a-grid-item>
|
||||||
<div class="file-grid-item" @click.stop="handleClickFile(item)">
|
<div class="file-grid-item" @click.stop="handleClickFile(item)" @dblclick="handleDblclickFile(item)">
|
||||||
<section class="file-grid-item__wrapper">
|
<section class="file-grid-item__wrapper">
|
||||||
<div class="file-icon">
|
<div class="file-icon">
|
||||||
<FileImage :data="item" :title="item.name"></FileImage>
|
<FileImage :data="item" :title="item.name"></FileImage>
|
||||||
@@ -51,6 +51,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'click', record: FileItem): void
|
(e: 'click', record: FileItem): void
|
||||||
|
(e: 'dblclick', record: FileItem): void
|
||||||
(e: 'select', record: FileItem): void
|
(e: 'select', record: FileItem): void
|
||||||
(e: 'right-menu-click', mode: string, item: FileItem): void
|
(e: 'right-menu-click', mode: string, item: FileItem): void
|
||||||
}>()
|
}>()
|
||||||
@@ -73,6 +74,11 @@ const handleClickFile = (item: FileItem) => {
|
|||||||
emit('click', item)
|
emit('click', item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 双击事件
|
||||||
|
const handleDblclickFile = (item: FileItem) => {
|
||||||
|
emit('dblclick', item)
|
||||||
|
}
|
||||||
|
|
||||||
// 选中事件
|
// 选中事件
|
||||||
const handleCheckFile = (item: FileItem) => {
|
const handleCheckFile = (item: FileItem) => {
|
||||||
emit('select', item)
|
emit('select', item)
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
</a-typography-paragraph>
|
</a-typography-paragraph>
|
||||||
</section>
|
</section>
|
||||||
<template #content>
|
<template #content>
|
||||||
<FileRightMenu :data="record" @click="handleRightMenuClick($event, record)"></FileRightMenu>
|
<FileRightMenu :data="record" @click="handleRightMenuClick($event, record)" @dblclick="handleDblclickFile(record)"></FileRightMenu>
|
||||||
</template>
|
</template>
|
||||||
</a-trigger>
|
</a-trigger>
|
||||||
</template>
|
</template>
|
||||||
@@ -77,6 +77,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'click', record: FileItem): void
|
(e: 'click', record: FileItem): void
|
||||||
|
(e: 'dblclick', record: FileItem): void
|
||||||
(e: 'select', record: FileItem): void
|
(e: 'select', record: FileItem): void
|
||||||
(e: 'right-menu-click', mode: string, item: FileItem): void
|
(e: 'right-menu-click', mode: string, item: FileItem): void
|
||||||
}>()
|
}>()
|
||||||
@@ -109,6 +110,11 @@ const handleRowClick: TableInstance['onRowClick'] = (record) => {
|
|||||||
emit('click', record as unknown as FileItem)
|
emit('click', record as unknown as FileItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 双击事件
|
||||||
|
const handleDblclickFile = (item: FileItem) => {
|
||||||
|
emit('dblclick', item)
|
||||||
|
}
|
||||||
|
|
||||||
// 右键菜单点击事件
|
// 右键菜单点击事件
|
||||||
const handleRightMenuClick = (mode: string, item: FileItem) => {
|
const handleRightMenuClick = (mode: string, item: FileItem) => {
|
||||||
emit('right-menu-click', mode, item)
|
emit('right-menu-click', mode, item)
|
||||||
|
@@ -38,6 +38,12 @@
|
|||||||
<icon-delete />
|
<icon-delete />
|
||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button type="primary" :disabled="!queryForm.absPath" @click="createDirModalVisible = !createDirModalVisible">
|
||||||
|
<template #icon>
|
||||||
|
<icon-folder />
|
||||||
|
</template>
|
||||||
|
<template #default>新建文件夹</template>
|
||||||
|
</a-button>
|
||||||
<a-button type="primary" @click="isBatchMode = !isBatchMode">
|
<a-button type="primary" @click="isBatchMode = !isBatchMode">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-select-all />
|
<icon-select-all />
|
||||||
@@ -62,14 +68,14 @@
|
|||||||
<FileGrid
|
<FileGrid
|
||||||
v-show="fileList.length && mode === 'grid'" :data="fileList" :is-batch-mode="isBatchMode"
|
v-show="fileList.length && mode === 'grid'" :data="fileList" :is-batch-mode="isBatchMode"
|
||||||
:selected-file-ids="selectedFileIds" @click="handleClickFile" @select="handleSelectFile"
|
:selected-file-ids="selectedFileIds" @click="handleClickFile" @select="handleSelectFile"
|
||||||
@right-menu-click="handleRightMenuClick"
|
@right-menu-click="handleRightMenuClick" @dblclick="handleDblclickFile"
|
||||||
></FileGrid>
|
></FileGrid>
|
||||||
|
|
||||||
<!-- 文件列表-列表模式 -->
|
<!-- 文件列表-列表模式 -->
|
||||||
<FileList
|
<FileList
|
||||||
v-show="fileList.length && mode === 'list'" :data="fileList" :is-batch-mode="isBatchMode"
|
v-show="fileList.length && mode === 'list'" :data="fileList" :is-batch-mode="isBatchMode"
|
||||||
:selected-file-ids="selectedFileIds" @click="handleClickFile" @select="handleSelectFile"
|
:selected-file-ids="selectedFileIds" @click="handleClickFile" @select="handleSelectFile"
|
||||||
@right-menu-click="handleRightMenuClick"
|
@right-menu-click="handleRightMenuClick" @dblclick="handleDblclickFile"
|
||||||
></FileList>
|
></FileList>
|
||||||
|
|
||||||
<a-empty v-if="!fileList.length" />
|
<a-empty v-if="!fileList.length" />
|
||||||
@@ -78,6 +84,11 @@
|
|||||||
<div class="pagination">
|
<div class="pagination">
|
||||||
<a-pagination v-bind="pagination" />
|
<a-pagination v-bind="pagination" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 弹出新建窗口 -->
|
||||||
|
<a-modal v-model:visible="createDirModalVisible" title="新建文件夹" @ok="handleCreateDir" @cancel="handleCancel">
|
||||||
|
<a-input v-model="newDirName" placeholder="请输入文件夹名称" size="large" allow-clear />
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -93,8 +104,8 @@ import {
|
|||||||
import FileGrid from './FileGrid.vue'
|
import FileGrid from './FileGrid.vue'
|
||||||
import useFileManage from './useFileManage'
|
import useFileManage from './useFileManage'
|
||||||
import { useTable } from '@/hooks'
|
import { useTable } from '@/hooks'
|
||||||
import { type FileItem, type FileQuery, deleteFile, listFile, uploadFile } from '@/apis'
|
import { type FileItem, type FileQuery, checkFile, createDir, deleteFile, listFile, uploadFile } from '@/apis'
|
||||||
import { ImageTypes, OfficeTypes } from '@/constant/file'
|
import { DirTypes, ImageTypes, OfficeTypes } from '@/constant/file'
|
||||||
import 'viewerjs/dist/viewer.css'
|
import 'viewerjs/dist/viewer.css'
|
||||||
import { downloadByUrl } from '@/utils/downloadFile'
|
import { downloadByUrl } from '@/utils/downloadFile'
|
||||||
import mittBus from '@/utils/mitt'
|
import mittBus from '@/utils/mitt'
|
||||||
@@ -114,9 +125,15 @@ const queryTypeOption = [{
|
|||||||
value: 'absPath',
|
value: 'absPath',
|
||||||
}]
|
}]
|
||||||
const queryType = ref<string>('name')
|
const queryType = ref<string>('name')
|
||||||
|
|
||||||
|
// 新建文件夹弹窗显示
|
||||||
|
const createDirModalVisible = ref<boolean>(false)
|
||||||
|
// 新文件名称
|
||||||
|
const newDirName = ref()
|
||||||
|
|
||||||
const queryForm = reactive<FileQuery>({
|
const queryForm = reactive<FileQuery>({
|
||||||
name: undefined,
|
name: undefined,
|
||||||
absPath: undefined,
|
absPath: '/',
|
||||||
type: route.query.type?.toString() !== '0' ? route.query.type?.toString() : undefined,
|
type: route.query.type?.toString() !== '0' ? route.query.type?.toString() : undefined,
|
||||||
sort: ['updateTime,desc'],
|
sort: ['updateTime,desc'],
|
||||||
})
|
})
|
||||||
@@ -180,6 +197,19 @@ const handleClickFile = (item: FileItem) => {
|
|||||||
previewFileAudioModal(item)
|
previewFileAudioModal(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 双击文件
|
||||||
|
const handleDblclickFile = (item: FileItem) => {
|
||||||
|
if (DirTypes.includes(item.extension)) {
|
||||||
|
if (item.absPath.endsWith('/')) {
|
||||||
|
queryForm.absPath = item.absPath + item.name
|
||||||
|
} else {
|
||||||
|
queryForm.absPath = `${item.absPath}/${item.name}`
|
||||||
|
}
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 下载文件
|
// 下载文件
|
||||||
const onDownload = async (fileInfo: FileItem) => {
|
const onDownload = async (fileInfo: FileItem) => {
|
||||||
const res = await downloadByUrl({
|
const res = await downloadByUrl({
|
||||||
@@ -242,6 +272,7 @@ const handleUpload = (options: RequestOption) => {
|
|||||||
const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options
|
const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options
|
||||||
onProgress(20)
|
onProgress(20)
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
formData.append('path', queryForm.absPath ?? '/')
|
||||||
formData.append(name as string, fileItem.file as Blob)
|
formData.append(name as string, fileItem.file as Blob)
|
||||||
try {
|
try {
|
||||||
const res = await uploadFile(formData)
|
const res = await uploadFile(formData)
|
||||||
@@ -272,6 +303,20 @@ onBeforeRouteUpdate((to) => {
|
|||||||
search()
|
search()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 新建文件夹弹窗窗口取消事件
|
||||||
|
const handleCancel = () => {
|
||||||
|
newDirName.value = undefined
|
||||||
|
createDirModalVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建文件夹弹窗窗口确认事件
|
||||||
|
const handleCreateDir = async () => {
|
||||||
|
const res = await createDir(queryForm.absPath ?? '/', newDirName.value)
|
||||||
|
newDirName.value = undefined
|
||||||
|
createDirModalVisible.value = false
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
search()
|
search()
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user