2026-04-02 12:06:51 +08:00
|
|
|
|
<template>
|
2026-04-01 22:39:11 +08:00
|
|
|
|
<div class="file-toolbar">
|
|
|
|
|
|
<!-- 第一行:视图切换 + 搜索框 + 上传/新建 -->
|
|
|
|
|
|
<div class="toolbar-top">
|
|
|
|
|
|
<el-button-group>
|
|
|
|
|
|
<el-button :type="currentMode === 'table' ? 'primary' : ''" @click="changeMode('table')">
|
|
|
|
|
|
<el-icon><List /></el-icon>
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button :type="currentMode === 'grid' ? 'primary' : ''" @click="changeMode('grid')">
|
|
|
|
|
|
<el-icon><Grid /></el-icon>
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</el-button-group>
|
|
|
|
|
|
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="searchText"
|
|
|
|
|
|
placeholder="搜索文件..."
|
|
|
|
|
|
clearable
|
|
|
|
|
|
class="search-input"
|
|
|
|
|
|
@keyup.enter="doSearch"
|
|
|
|
|
|
@input="onSearchInput"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix><el-icon><Search /></el-icon></template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
|
|
|
|
|
|
<el-button-group v-if="menuType === 'my-files'">
|
|
|
|
|
|
<el-button type="primary" @click="$emit('upload')">
|
|
|
|
|
|
<el-icon><Upload /></el-icon>
|
|
|
|
|
|
<span style="margin-left: 4px">上传文件</span>
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button @click="$emit('newFolder')">
|
|
|
|
|
|
<el-icon><FolderAdd /></el-icon>
|
2026-04-02 12:06:51 +08:00
|
|
|
|
<span style="margin-left: 4px">新建目录</span>
|
2026-04-01 22:39:11 +08:00
|
|
|
|
</el-button>
|
|
|
|
|
|
</el-button-group>
|
|
|
|
|
|
|
|
|
|
|
|
<el-button type="danger" v-if="menuType === 'trash'" @click="$emit('emptyTrash')">
|
|
|
|
|
|
<el-icon><Delete /></el-icon>
|
|
|
|
|
|
<span style="margin-left: 4px">清空回收站</span>
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-04-02 12:06:51 +08:00
|
|
|
|
<!-- 第二行:面包屑导航 -->
|
2026-04-02 15:02:54 +08:00
|
|
|
|
<div class="toolbar-bottom">
|
2026-04-02 12:06:51 +08:00
|
|
|
|
<el-icon class="breadcrumb-icon"><Folder /></el-icon>
|
|
|
|
|
|
<el-breadcrumb separator="/" v-if="folderStack.length > 0">
|
|
|
|
|
|
<el-breadcrumb-item>
|
|
|
|
|
|
<span class="breadcrumb-link" @click="$emit('goBack', -1)">根目录</span>
|
|
|
|
|
|
</el-breadcrumb-item>
|
|
|
|
|
|
<el-breadcrumb-item
|
|
|
|
|
|
v-for="(folder, index) in folderStack"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span
|
|
|
|
|
|
class="breadcrumb-link"
|
|
|
|
|
|
:class="{ 'is-last': index === folderStack.length - 1 }"
|
|
|
|
|
|
@click="handleBreadcrumbClick(index)"
|
|
|
|
|
|
>{{ folder.name }}</span>
|
|
|
|
|
|
</el-breadcrumb-item>
|
|
|
|
|
|
</el-breadcrumb>
|
|
|
|
|
|
<span v-else class="current-path">根目录</span>
|
2026-04-01 22:39:11 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref, watch } from 'vue'
|
2026-04-02 12:06:51 +08:00
|
|
|
|
import { List, Grid, Upload, FolderAdd, Delete, Search, Folder } from '@element-plus/icons-vue'
|
2026-04-01 22:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
viewMode: { type: String, default: 'table' },
|
|
|
|
|
|
searchKeyword: { type: String, default: '' },
|
|
|
|
|
|
menuType: { type: String, default: 'my-files' },
|
|
|
|
|
|
currentPath: { type: String, default: '/' },
|
2026-04-02 12:06:51 +08:00
|
|
|
|
showBack: { type: Boolean, default: false },
|
|
|
|
|
|
folderStack: { type: Array, default: () => [] }
|
2026-04-01 22:39:11 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits([
|
|
|
|
|
|
'update:viewMode',
|
|
|
|
|
|
'update:searchKeyword',
|
|
|
|
|
|
'search',
|
|
|
|
|
|
'refresh',
|
|
|
|
|
|
'upload',
|
|
|
|
|
|
'newFolder',
|
|
|
|
|
|
'emptyTrash',
|
|
|
|
|
|
'goBack'
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
const currentMode = ref(props.viewMode)
|
|
|
|
|
|
const searchText = ref(props.searchKeyword)
|
|
|
|
|
|
|
|
|
|
|
|
watch(() => props.viewMode, (val) => {
|
|
|
|
|
|
currentMode.value = val
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
watch(() => props.searchKeyword, (val) => {
|
|
|
|
|
|
searchText.value = val
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const changeMode = (mode) => {
|
|
|
|
|
|
currentMode.value = mode
|
|
|
|
|
|
emit('update:viewMode', mode)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onSearchInput = (val) => {
|
|
|
|
|
|
emit('update:searchKeyword', val)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const doSearch = () => {
|
|
|
|
|
|
emit('search')
|
|
|
|
|
|
}
|
2026-04-02 12:06:51 +08:00
|
|
|
|
|
|
|
|
|
|
const handleBreadcrumbClick = (index) => {
|
|
|
|
|
|
// 点击当前项不跳转
|
|
|
|
|
|
if (index === props.folderStack.length - 1) return
|
|
|
|
|
|
emit('goBack', index)
|
|
|
|
|
|
}
|
2026-04-01 22:39:11 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.file-toolbar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border-bottom: 1px solid #e4e7ed;
|
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar-top {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.search-input {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar-bottom {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-04-02 12:06:51 +08:00
|
|
|
|
min-height: 32px;
|
|
|
|
|
|
padding-top: 12px;
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
border-top: 1px solid #ebeef5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.breadcrumb-icon {
|
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
color: #f7b32b;
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.current-path {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.breadcrumb-link {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.breadcrumb-link:hover {
|
|
|
|
|
|
color: #66b1ff;
|
2026-04-01 22:39:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 12:06:51 +08:00
|
|
|
|
.breadcrumb-link.is-last {
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-breadcrumb) {
|
2026-04-01 22:39:11 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-02 12:06:51 +08:00
|
|
|
|
:deep(.el-breadcrumb__item) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-breadcrumb__inner) {
|
2026-04-01 22:39:11 +08:00
|
|
|
|
color: #606266;
|
2026-04-02 12:06:51 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-breadcrumb__separator) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-04-01 22:39:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|