A:调整文件夹

This commit is contained in:
jimmy committed 2025-09-30 21:58:22 +08:00
1 parent 7af4f31538
commit bf5e44dca3
6 files changed
+372 -163

No files matched your search

@@ -54,10 +54,17 @@
<button <button
:class="['btn-layout', { active: layoutMode === 'horizontal' }]" :class="['btn-layout', { active: layoutMode === 'horizontal' }]"
@click="layoutMode = 'horizontal'" @click="layoutMode = 'horizontal'"
title="横向双面板" title="左右双面板"
> >
▭ ▭
</button> </button>
<button
:class="['btn-layout', { active: layoutMode === 'vertical' }]"
@click="layoutMode = 'vertical'"
title="上下双面板"
>
▯
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -73,13 +80,13 @@
/> />
<div <div
v-if="layoutMode === 'horizontal'" v-if="layoutMode === 'horizontal' || layoutMode === 'vertical'"
class="splitter" :class="['splitter', layoutMode === 'vertical' ? 'splitter-vertical' : '']"
@mousedown="handleSplitterMouseDown" @mousedown="handleSplitterMouseDown"
></div> ></div>
<FilePane <FilePane
v-if="layoutMode === 'horizontal'" v-if="layoutMode === 'horizontal' || layoutMode === 'vertical'"
pane-id="right" pane-id="right"
:view-mode="viewMode" :view-mode="viewMode"
:thumbnail-size="thumbnailSize" :thumbnail-size="thumbnailSize"
@@ -105,7 +112,8 @@ const isDraggingSplitter = ref(false)
function handleSplitterMouseDown(event: MouseEvent) { function handleSplitterMouseDown(event: MouseEvent) {
isDraggingSplitter.value = true isDraggingSplitter.value = true
document.body.style.cursor = 'col-resize' const isVertical = layoutMode.value === 'vertical'
document.body.style.cursor = isVertical ? 'row-resize' : 'col-resize'
document.body.style.userSelect = 'none' document.body.style.userSelect = 'none'
const mainContent = document.querySelector('.main-content') as HTMLElement const mainContent = document.querySelector('.main-content') as HTMLElement
@@ -115,11 +123,21 @@ function handleSplitterMouseDown(event: MouseEvent) {
if (!isDraggingSplitter.value) return if (!isDraggingSplitter.value) return
const rect = mainContent.getBoundingClientRect() const rect = mainContent.getBoundingClientRect()
const x = e.clientX - rect.left
const percentage = (x / rect.width) * 100
if (percentage > 20 && percentage < 80) { if (isVertical) {
mainContent.style.gridTemplateColumns = `${percentage}% 4px 1fr` const y = e.clientY - rect.top
const percentage = (y / rect.height) * 100
if (percentage > 20 && percentage < 80) {
mainContent.style.gridTemplateRows = `${percentage}% 4px 1fr`
}
} else {
const x = e.clientX - rect.left
const percentage = (x / rect.width) * 100
if (percentage > 20 && percentage < 80) {
mainContent.style.gridTemplateColumns = `${percentage}% 4px 1fr`
}
} }
} }
@@ -140,7 +158,7 @@ function handleSplitterMouseDown(event: MouseEvent) {
.file-manager { .file-manager {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100%;
background: #f9fafb; background: #f9fafb;
} }
@@ -235,11 +253,20 @@ function handleSplitterMouseDown(event: MouseEvent) {
grid-template-columns: 1fr 4px 1fr; grid-template-columns: 1fr 4px 1fr;
} }
.main-content.layout-vertical {
display: grid;
grid-template-rows: 1fr 4px 1fr;
}
.splitter { .splitter {
background: #d1d5db; background: #d1d5db;
cursor: col-resize; cursor: col-resize;
} }
.splitter.splitter-vertical {
cursor: row-resize;
}
.splitter:hover { .splitter:hover {
background: #9ca3af; background: #9ca3af;
} }
@@ -70,7 +70,7 @@
@drop="handlePaneDrop" @drop="handlePaneDrop"
> >
<!-- Grid View --> <!-- Grid View -->
<div v-if="viewMode === 'grid'" :class="['file-grid', `grid-${thumbnailSize}`]"> <div v-if="viewMode === 'grid'" :class="['file-grid', `grid-${thumbnailSize}`]" :style="gridItemSize">
<div <div
v-for="(item, index) in visibleItems" v-for="(item, index) in visibleItems"
:key="item.name" :key="item.name"
@@ -211,6 +211,7 @@ const selection = useFileSelection()
const searchQuery = ref('') const searchQuery = ref('')
const pathEditMode = ref(false) const pathEditMode = ref(false)
const pathEditValue = ref('') const pathEditValue = ref('')
const zoomLevel = ref(100) // 缩放级别:50-200%
// Context Menu state // Context Menu state
const contextMenu = ref({ const contextMenu = ref({
@@ -237,7 +238,8 @@ const dragSelection = ref({
currentX: 0, currentX: 0,
currentY: 0, currentY: 0,
initialSelections: new Set<string>(), initialSelections: new Set<string>(),
ctrlKey: false ctrlKey: false,
justFinished: false
}) })
// Auto scroll state // Auto scroll state
@@ -486,13 +488,28 @@ function handleMouseDown(event: MouseEvent) {
currentX: event.clientX - rect.left + scrollEl.scrollLeft, currentX: event.clientX - rect.left + scrollEl.scrollLeft,
currentY: event.clientY - rect.top + scrollEl.scrollTop, currentY: event.clientY - rect.top + scrollEl.scrollTop,
initialSelections: new Set(selection.selectedItems.value), initialSelections: new Set(selection.selectedItems.value),
ctrlKey: event.ctrlKey || event.metaKey ctrlKey: event.ctrlKey || event.metaKey,
justFinished: false
} }
if (!dragSelection.value.ctrlKey) { if (!dragSelection.value.ctrlKey) {
selection.clearSelection() selection.clearSelection()
} }
// 在 document 级别监听 mousemove 和 mouseup
const onDocumentMouseMove = (e: MouseEvent) => {
handleMouseMove(e)
}
const onDocumentMouseUp = (e: MouseEvent) => {
handleMouseUp()
document.removeEventListener('mousemove', onDocumentMouseMove)
document.removeEventListener('mouseup', onDocumentMouseUp)
}
document.addEventListener('mousemove', onDocumentMouseMove)
document.addEventListener('mouseup', onDocumentMouseUp)
event.preventDefault() event.preventDefault()
} }
@@ -503,8 +520,27 @@ function handleMouseMove(event: MouseEvent) {
if (!rect) return if (!rect) return
const scrollEl = contentAreaRef.value! const scrollEl = contentAreaRef.value!
dragSelection.value.currentX = event.clientX - rect.left + scrollEl.scrollLeft
dragSelection.value.currentY = event.clientY - rect.top + scrollEl.scrollTop // 计算鼠标相对于内容区域的位置(包含滚动偏移)
// 鼠标在视口中的位置 + 滚动偏移 = 在文档中的绝对位置
const rawX = event.clientX - rect.left + scrollEl.scrollLeft
const rawY = event.clientY - rect.top + scrollEl.scrollTop
// 限制坐标在合理范围内
// 当鼠标超出边界时,坐标应该停留在边界上,而不是继续增长
const maxScrollLeft = Math.max(0, scrollEl.scrollWidth - scrollEl.clientWidth)
const maxScrollTop = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight)
// 最大坐标 = 可见区域大小 + 最大滚动距离
const maxX = scrollEl.clientWidth + maxScrollLeft
const maxY = scrollEl.clientHeight + maxScrollTop
const relativeX = Math.max(0, Math.min(rawX, maxX))
const relativeY = Math.max(0, Math.min(rawY, maxY))
// 存储坐标
dragSelection.value.currentX = relativeX
dragSelection.value.currentY = relativeY
// 自动滚动检测 // 自动滚动检测
checkAutoScroll(event) checkAutoScroll(event)
@@ -521,6 +557,12 @@ function handleMouseUp() {
dragSelection.value.isSelecting = false dragSelection.value.isSelecting = false
autoScroll.value.isScrolling = false autoScroll.value.isScrolling = false
// 标记刚完成拖拽,防止 handleContentClick 清除选择
dragSelection.value.justFinished = true
setTimeout(() => {
dragSelection.value.justFinished = false
}, 100)
// 设置焦点到最后选中的项 // 设置焦点到最后选中的项
if (selection.selectedItems.value.size > 0) { if (selection.selectedItems.value.size > 0) {
const lastSelected = Array.from(selection.selectedItems.value).pop()! const lastSelected = Array.from(selection.selectedItems.value).pop()!
@@ -537,21 +579,45 @@ function checkAutoScroll(event: MouseEvent) {
if (!contentAreaRef.value) return if (!contentAreaRef.value) return
const rect = contentAreaRef.value.getBoundingClientRect() const rect = contentAreaRef.value.getBoundingClientRect()
const scrollEl = contentAreaRef.value
const threshold = 50 const threshold = 50
const baseScrollSpeed = 10
let dx = 0 let dx = 0
let dy = 0 let dy = 0
if (event.clientY < rect.top + threshold) { // 检查垂直滚动
dy = -5 if (event.clientY < rect.top) {
dy = -baseScrollSpeed
} else if (event.clientY < rect.top + threshold) {
const distance = event.clientY - rect.top
const ratio = 1 - distance / threshold
dy = -Math.max(3, baseScrollSpeed * ratio)
} else if (event.clientY > rect.bottom) {
dy = baseScrollSpeed
} else if (event.clientY > rect.bottom - threshold) { } else if (event.clientY > rect.bottom - threshold) {
dy = 5 const distance = rect.bottom - event.clientY
const ratio = 1 - distance / threshold
dy = Math.max(3, baseScrollSpeed * ratio)
} }
if (event.clientX < rect.left + threshold) { // 检查水平滚动 - 只在确实有横向滚动空间时才启用
dx = -5 const hasHorizontalScroll = scrollEl.scrollWidth > scrollEl.clientWidth
} else if (event.clientX > rect.right - threshold) {
dx = 5 if (hasHorizontalScroll) {
if (event.clientX < rect.left) {
dx = -baseScrollSpeed
} else if (event.clientX < rect.left + threshold) {
const distance = event.clientX - rect.left
const ratio = 1 - distance / threshold
dx = -Math.max(3, baseScrollSpeed * ratio)
} else if (event.clientX > rect.right) {
dx = baseScrollSpeed
} else if (event.clientX > rect.right - threshold) {
const distance = rect.right - event.clientX
const ratio = 1 - distance / threshold
dx = Math.max(3, baseScrollSpeed * ratio)
}
} }
if (dx !== 0 || dy !== 0) { if (dx !== 0 || dy !== 0) {
@@ -569,23 +635,56 @@ function checkAutoScroll(event: MouseEvent) {
// 开始自动滚动 // 开始自动滚动
function startAutoScroll() { function startAutoScroll() {
let frameId: number | null = null
function scroll() { function scroll() {
if (!autoScroll.value.isScrolling || !contentAreaRef.value) return if (!autoScroll.value.isScrolling || !contentAreaRef.value) {
if (frameId !== null) {
cancelAnimationFrame(frameId)
frameId = null
}
return
}
contentAreaRef.value.scrollLeft += autoScroll.value.direction.x const scrollEl = contentAreaRef.value
contentAreaRef.value.scrollTop += autoScroll.value.direction.y
// 更新拖拽坐标 // 计算新的滚动位置,并限制在有效范围内
const rect = contentAreaRef.value.getBoundingClientRect() const maxScrollLeft = Math.max(0, scrollEl.scrollWidth - scrollEl.clientWidth)
dragSelection.value.currentX += autoScroll.value.direction.x const maxScrollTop = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight)
dragSelection.value.currentY += autoScroll.value.direction.y
updateDragSelection() let newScrollLeft = scrollEl.scrollLeft + autoScroll.value.direction.x
let newScrollTop = scrollEl.scrollTop + autoScroll.value.direction.y
requestAnimationFrame(scroll) // 限制滚动范围
newScrollLeft = Math.max(0, Math.min(newScrollLeft, maxScrollLeft))
newScrollTop = Math.max(0, Math.min(newScrollTop, maxScrollTop))
// 计算实际滚动距离
const actualDx = newScrollLeft - scrollEl.scrollLeft
const actualDy = newScrollTop - scrollEl.scrollTop
// 应用滚动
scrollEl.scrollLeft = newScrollLeft
scrollEl.scrollTop = newScrollTop
// 更新拖拽坐标(只在实际发生滚动时更新)
if (actualDx !== 0 || actualDy !== 0) {
dragSelection.value.currentX += actualDx
dragSelection.value.currentY += actualDy
// 确保坐标不超出内容范围
const maxX = scrollEl.clientWidth + maxScrollLeft
const maxY = scrollEl.clientHeight + maxScrollTop
dragSelection.value.currentX = Math.max(0, Math.min(dragSelection.value.currentX, maxX))
dragSelection.value.currentY = Math.max(0, Math.min(dragSelection.value.currentY, maxY))
updateDragSelection()
}
frameId = requestAnimationFrame(scroll)
} }
requestAnimationFrame(scroll) frameId = requestAnimationFrame(scroll)
} }
// 更新框选的项 // 更新框选的项
@@ -645,6 +744,11 @@ function updateDragSelection() {
} }
function handleContentClick(event: MouseEvent) { function handleContentClick(event: MouseEvent) {
// 如果刚完成拖拽选择,不要清除选择
if (dragSelection.value.justFinished) {
return
}
const target = event.target as HTMLElement const target = event.target as HTMLElement
if (!target.closest('.file-item') && !target.closest('.list-item')) { if (!target.closest('.file-item') && !target.closest('.list-item')) {
if (!event.ctrlKey) { if (!event.ctrlKey) {
@@ -712,8 +816,14 @@ function handleContextMenuAction(action: string, params?: any) {
// Tooltip 处理 // Tooltip 处理
function handleItemMouseEnter(event: MouseEvent, item: FileItem) { function handleItemMouseEnter(event: MouseEvent, item: FileItem) {
if (dragState.value.isDragging) return if (dragState.value.isDragging) return
const tooltip = `名称: ${item.name}\n类型: ${item.type === 'folder' ? '文件夹' : '文件'}\n${item.size ? '大小: ' + item.size : ''}\n${item.date ? '日期: ' + item.date : ''}`
tooltipRef.value?.show(tooltip.trim(), event.clientX, event.clientY) const lines: string[] = []
lines.push(`类型: ${item.type === 'folder' ? '文件夹' : '文件'}`)
if (item.size) lines.push(`大小: ${item.size}`)
if (item.date) lines.push(`修改日期: ${item.date}`)
const tooltip = lines.join('\n')
tooltipRef.value?.show(tooltip, event.clientX, event.clientY)
} }
function handleItemMouseLeave() { function handleItemMouseLeave() {
@@ -893,22 +1003,56 @@ function getGridColumns(): number {
return 1 return 1
} }
// 处理Ctrl+滚轮缩放
function handleWheel(event: WheelEvent) {
if (!event.ctrlKey) return
event.preventDefault()
const delta = event.deltaY > 0 ? -10 : 10
const newZoom = Math.max(50, Math.min(200, zoomLevel.value + delta))
zoomLevel.value = newZoom
}
// 计算动态网格大小
const gridItemSize = computed(() => {
if (props.viewMode !== 'grid') return {}
const baseSize = props.thumbnailSize === 'small' ? 80 :
props.thumbnailSize === 'medium' ? 100 : 130
const size = Math.round(baseSize * (zoomLevel.value / 100))
return {
'--grid-item-size': `${size}px`
}
})
// 监听搜索变化清除选择 // 监听搜索变化清除选择
watch(searchQuery, () => { watch(searchQuery, () => {
selection.clearSelection() selection.clearSelection()
}) })
// 关闭breadcrumb dropdown // 关闭breadcrumb dropdown 和 添加滚轮监听
onMounted(() => { onMounted(() => {
document.addEventListener('click', () => { const closeDropdown = () => {
breadcrumbDropdown.value.visible = false breadcrumbDropdown.value.visible = false
}) }
document.addEventListener('click', closeDropdown)
if (contentAreaRef.value) {
contentAreaRef.value.addEventListener('wheel', handleWheel, { passive: false })
}
}) })
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener('click', () => { document.removeEventListener('click', () => {
breadcrumbDropdown.value.visible = false breadcrumbDropdown.value.visible = false
}) })
if (contentAreaRef.value) {
contentAreaRef.value.removeEventListener('wheel', handleWheel)
}
}) })
// 键盘导航 // 键盘导航
@@ -972,6 +1116,8 @@ useKeyboardNav({
flex-direction: column; flex-direction: column;
background: white; background: white;
transition: opacity 0.15s; transition: opacity 0.15s;
height: 100%;
overflow: hidden;
} }
.file-pane.inactive { .file-pane.inactive {
@@ -1232,6 +1378,7 @@ useKeyboardNav({
/* Content Area */ /* Content Area */
.content-area { .content-area {
flex: 1; flex: 1;
min-height: 0;
padding: 16px; padding: 16px;
overflow: auto; overflow: auto;
position: relative; position: relative;
@@ -1247,44 +1394,46 @@ useKeyboardNav({
/* Grid View */ /* Grid View */
.file-grid { .file-grid {
display: grid; display: grid;
gap: 16px; gap: 8px;
--grid-item-size: 100px; /* 默认值,会被动态覆盖 */
} }
.file-grid.grid-small { .file-grid.grid-small {
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(var(--grid-item-size, 80px), 1fr));
} }
.file-grid.grid-medium { .file-grid.grid-medium {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(var(--grid-item-size, 100px), 1fr));
} }
.file-grid.grid-large { .file-grid.grid-large {
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(var(--grid-item-size, 130px), 1fr));
} }
.file-item { .file-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 12px; justify-content: center;
padding: 8px;
background: white; background: white;
border: 2px solid #e5e7eb; border: 1px solid #e5e7eb;
border-radius: 8px; border-radius: 4px;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
user-select: none; user-select: none;
aspect-ratio: 1 / 1;
position: relative;
} }
.file-item:hover { .file-item:hover {
background: #f9fafb; background: #f3f4f6;
transform: translateY(-1px); border-color: #d1d5db;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
} }
.file-item.selected { .file-item.selected {
background: #dbeafe; background: #dbeafe;
border-color: #3b82f6; border-color: #3b82f6;
box-shadow: 0 0 0 1px #3b82f6;
} }
.file-item.focused { .file-item.focused {
@@ -1305,16 +1454,23 @@ useKeyboardNav({
} }
.file-icon { .file-icon {
font-size: 32px; font-size: 40px;
margin-bottom: 8px; margin-bottom: 4px;
flex-shrink: 0;
} }
.file-name { .file-name {
font-size: 13px; font-size: 12px;
text-align: center; text-align: center;
word-break: break-word; word-break: break-word;
font-weight: 500;
color: #374151; color: #374151;
line-height: 1.3;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
} }
.file-name :deep(mark) { .file-name :deep(mark) {
@@ -1326,36 +1482,37 @@ useKeyboardNav({
} }
.file-size { .file-size {
font-size: 11px; display: none;
color: #9ca3af;
margin-top: 4px;
} }
/* List View */ /* List View */
.file-list { .file-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1px; gap: 0;
} }
.list-item { .list-item {
display: grid; display: grid;
grid-template-columns: 40px 1fr 100px 120px; grid-template-columns: 32px 1fr 100px 140px;
align-items: center; align-items: center;
padding: 10px 12px; padding: 4px 8px;
background: white; background: white;
border-bottom: 1px solid #f3f4f6; border: 1px solid transparent;
cursor: pointer; cursor: pointer;
transition: background 0.15s; transition: background 0.1s;
user-select: none; user-select: none;
min-height: 32px;
} }
.list-item:hover { .list-item:hover {
background: #f9fafb; background: #f3f4f6;
border-color: #e5e7eb;
} }
.list-item.selected { .list-item.selected {
background: #dbeafe; background: #cce8ff;
border-color: #99d1ff;
} }
.list-item.drop-target { .list-item.drop-target {
@@ -1370,12 +1527,15 @@ useKeyboardNav({
} }
.list-icon { .list-icon {
font-size: 20px; font-size: 18px;
} }
.list-name { .list-name {
font-size: 14px; font-size: 13px;
color: #374151; color: #1f2937;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.list-name :deep(mark) { .list-name :deep(mark) {
@@ -1387,7 +1547,7 @@ useKeyboardNav({
} }
.list-size, .list-date { .list-size, .list-date {
font-size: 13px; font-size: 12px;
color: #6b7280; color: #6b7280;
} }
@@ -18,7 +18,7 @@ const content = ref('')
const position = ref({ x: 0, y: 0 }) const position = ref({ x: 0, y: 0 })
const timeout = ref<number>() const timeout = ref<number>()
function show(text: string, x: number, y: number, delay = 500) { function show(text: string, x: number, y: number, delay = 1000) {
clearTimeout(timeout.value) clearTimeout(timeout.value)
timeout.value = window.setTimeout(() => { timeout.value = window.setTimeout(() => {
@@ -42,16 +42,17 @@ defineExpose({
<style scoped> <style scoped>
.tooltip { .tooltip {
position: fixed; position: fixed;
background: rgba(0, 0, 0, 0.85); background: white;
color: white; color: #374151;
padding: 6px 10px; padding: 8px 12px;
border-radius: 6px; border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 12px; font-size: 12px;
pointer-events: none; pointer-events: none;
z-index: 100; z-index: 100;
white-space: nowrap; white-space: pre-line;
max-width: 300px; max-width: 280px;
word-wrap: break-word; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
white-space: normal; line-height: 1.5;
} }
</style> </style>
@@ -5,103 +5,126 @@ export const mockFolderStructure: FolderStructure = {
Home: { Home: {
name: 'Home', name: 'Home',
type: 'folder', type: 'folder',
children: { children: (() => {
'My Photos': { const homeItems: Record<string, any> = {
name: 'My Photos', 'My Photos': {
type: 'folder', name: 'My Photos',
children: { type: 'folder',
'Vacation 2023': { children: {
name: 'Vacation 2023', 'Vacation 2023': {
type: 'folder', name: 'Vacation 2023',
children: { type: 'folder',
Beach: { children: {
name: 'Beach', Beach: {
type: 'folder', name: 'Beach',
children: { type: 'folder',
'photo1.jpg': { name: 'photo1.jpg', type: 'photo', size: '2.4 MB', date: '2023-07-15' }, children: {
'photo2.jpg': { name: 'photo2.jpg', type: 'photo', size: '3.1 MB', date: '2023-07-15' }, 'photo1.jpg': { name: 'photo1.jpg', type: 'photo', size: '2.4 MB', date: '2023-07-15' },
'photo3.jpg': { name: 'photo3.jpg', type: 'photo', size: '2.8 MB', date: '2023-07-16' }, 'photo2.jpg': { name: 'photo2.jpg', type: 'photo', size: '3.1 MB', date: '2023-07-15' },
'beach_sunset.jpg': { name: 'beach_sunset.jpg', type: 'photo', size: '5.2 MB', date: '2023-07-17' }, 'photo3.jpg': { name: 'photo3.jpg', type: 'photo', size: '2.8 MB', date: '2023-07-16' },
'waves.jpg': { name: 'waves.jpg', type: 'photo', size: '3.8 MB', date: '2023-07-18' } 'beach_sunset.jpg': { name: 'beach_sunset.jpg', type: 'photo', size: '5.2 MB', date: '2023-07-17' },
} 'waves.jpg': { name: 'waves.jpg', type: 'photo', size: '3.8 MB', date: '2023-07-18' }
}, }
Mountains: { },
name: 'Mountains', Mountains: {
type: 'folder', name: 'Mountains',
children: { type: 'folder',
'sunrise.jpg': { name: 'sunrise.jpg', type: 'photo', size: '4.2 MB', date: '2023-07-20' }, children: {
'peak_view.jpg': { name: 'peak_view.jpg', type: 'photo', size: '6.1 MB', date: '2023-07-21' } 'sunrise.jpg': { name: 'sunrise.jpg', type: 'photo', size: '4.2 MB', date: '2023-07-20' },
'peak_view.jpg': { name: 'peak_view.jpg', type: 'photo', size: '6.1 MB', date: '2023-07-21' }
}
} }
} }
}
},
Family: {
name: 'Family',
type: 'folder',
children: {
'dinner.jpg': { name: 'dinner.jpg', type: 'photo', size: '2.2 MB', date: '2023-08-15' },
'family_portrait.jpg': { name: 'family_portrait.jpg', type: 'photo', size: '4.1 MB', date: '2023-08-20' }
}
}
}
},
'Camera Roll': {
name: 'Camera Roll',
type: 'folder',
children: (() => {
const items: Record<string, any> = {
Recent: {
name: 'Recent',
type: 'folder',
children: {}
}, },
Favorites: { Family: {
name: 'Favorites', name: 'Family',
type: 'folder', type: 'folder',
children: {} children: {
'dinner.jpg': { name: 'dinner.jpg', type: 'photo', size: '2.2 MB', date: '2023-08-15' },
'family_portrait.jpg': { name: 'family_portrait.jpg', type: 'photo', size: '4.1 MB', date: '2023-08-20' }
}
} }
} }
},
// 生成100张照片 'Camera Roll': {
for (let i = 1; i <= 100; i++) { name: 'Camera Roll',
const size = (Math.random() * 3 + 1).toFixed(1) type: 'folder',
const day = Math.floor(Math.random() * 30) + 1 children: (() => {
items[`IMG_${String(i).padStart(4, '0')}.jpg`] = { const items: Record<string, any> = {
name: `IMG_${String(i).padStart(4, '0')}.jpg`, Recent: {
type: 'photo', name: 'Recent',
size: `${size} MB`, type: 'folder',
date: `2023-09-${String(day).padStart(2, '0')}` children: {}
},
Favorites: {
name: 'Favorites',
type: 'folder',
children: {}
}
} }
}
// Recent文件夹添加照片 // 生成100张照片
for (let i = 1; i <= 30; i++) { for (let i = 1; i <= 100; i++) {
const size = (Math.random() * 3 + 1).toFixed(1) const size = (Math.random() * 3 + 1).toFixed(1)
items.Recent.children[`Recent_${String(i).padStart(3, '0')}.jpg`] = { const day = Math.floor(Math.random() * 30) + 1
name: `Recent_${String(i).padStart(3, '0')}.jpg`, items[`IMG_${String(i).padStart(4, '0')}.jpg`] = {
type: 'photo', name: `IMG_${String(i).padStart(4, '0')}.jpg`,
size: `${size} MB`, type: 'photo',
date: `2023-12-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}` size: `${size} MB`,
date: `2023-09-${String(day).padStart(2, '0')}`
}
} }
}
return items // Recent文件夹添加照片
})() for (let i = 1; i <= 30; i++) {
}, const size = (Math.random() * 3 + 1).toFixed(1)
'Work Projects': { items.Recent.children[`Recent_${String(i).padStart(3, '0')}.jpg`] = {
name: 'Work Projects', name: `Recent_${String(i).padStart(3, '0')}.jpg`,
type: 'folder', type: 'photo',
children: { size: `${size} MB`,
Screenshots: { date: `2023-12-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`
name: 'Screenshots', }
type: 'folder', }
children: {
'design1.png': { name: 'design1.png', type: 'photo', size: '1.2 MB', date: '2023-09-01' }, return items
'design2.png': { name: 'design2.png', type: 'photo', size: '1.5 MB', date: '2023-09-02' } })()
},
'Work Projects': {
name: 'Work Projects',
type: 'folder',
children: {
Screenshots: {
name: 'Screenshots',
type: 'folder',
children: {
'design1.png': { name: 'design1.png', type: 'photo', size: '1.2 MB', date: '2023-09-01' },
'design2.png': { name: 'design2.png', type: 'photo', size: '1.5 MB', date: '2023-09-02' }
}
} }
} }
} }
} }
}
// 在Home文件夹添加更多文件夹和文件,确保有足够内容触发滚动
for (let i = 1; i <= 50; i++) {
homeItems[`Folder_${String(i).padStart(2, '0')}`] = {
name: `Folder_${String(i).padStart(2, '0')}`,
type: 'folder',
children: {}
}
}
for (let i = 1; i <= 50; i++) {
const size = (Math.random() * 5 + 1).toFixed(1)
homeItems[`File_${String(i).padStart(2, '0')}.jpg`] = {
name: `File_${String(i).padStart(2, '0')}.jpg`,
type: 'photo',
size: `${size} MB`,
date: `2024-01-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}`
}
}
return homeItems
})()
} }
} }
@@ -298,15 +298,13 @@ const handleMenuSelect = (key: string) => {
.layout-content { .layout-content {
height: 100%; height: 100%;
overflow: auto; overflow: hidden;
flex: 1; flex: 1;
background-color: var(--content-bg); background-color: var(--content-bg);
} }
.content-wrapper { .content-wrapper {
padding: 24px;
height: 100%; height: 100%;
min-height: calc(100vh - 64px);
box-sizing: border-box; box-sizing: border-box;
} }
@@ -11,7 +11,7 @@ import FileManager from '@/components/FileManager/FileManager.vue'
<style scoped> <style scoped>
.field-view { .field-view {
width: 100%; width: 100%;
height: 100vh; height: 100%;
overflow: hidden; overflow: hidden;
} }
</style> </style>