refactor(frontend): extract shared modules, thin views, delete dead code
Shared utilities: - Add src/utils/formatters.js — formatDate, formatSize, providerColor, providerBg, providerLabel; all components import from here, no inline duplicates - Add src/components/ui/TreeItem.vue — generic expand/collapse tree node; FolderTreeItem, CloudFolderTreeItem, CloudProviderTreeItem now wrap it - Add src/components/storage/StorageBrowser.vue — unified file browser grid used by both FileManagerView and CloudFolderView View refactor (thin data-providers): - FileManagerView.vue: stripped to props + event wiring; all layout moved to StorageBrowser - CloudFolderView.vue: same treatment — feeds props into StorageBrowser - All tree sidebar components delegate expand/collapse to TreeItem.vue Dead code removed: - Delete HomeView.vue — no active route, replaced by FileManagerView - Delete FolderView.vue — no active route, logic merged into FileManagerView Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
a548266461
commit
cce70b2ef6
@@ -1,72 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Row -->
|
||||
<div
|
||||
class="flex items-center group"
|
||||
:style="{ paddingLeft: `${depth * 12}px` }"
|
||||
>
|
||||
<!-- Expand/collapse arrow (only for directories) -->
|
||||
<button
|
||||
<TreeItem
|
||||
:label="folder.name"
|
||||
:expandable="folder.is_dir"
|
||||
:load-children="loadChildren"
|
||||
:depth="depth"
|
||||
@select="navigate"
|
||||
>
|
||||
<template #icon>
|
||||
<svg
|
||||
v-if="folder.is_dir"
|
||||
@click.prevent.stop="toggleExpand"
|
||||
class="w-5 h-5 flex items-center justify-center text-gray-400 hover:text-gray-600 shrink-0 transition-colors"
|
||||
:aria-label="expanded ? 'Collapse ' + folder.name : 'Expand ' + folder.name"
|
||||
class="w-4 h-4 shrink-0 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<svg
|
||||
class="w-3 h-3 transition-transform duration-150"
|
||||
:class="{ 'rotate-90': expanded }"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
<!-- Spacer for non-directory items -->
|
||||
<span v-else class="w-5 h-5 shrink-0"></span>
|
||||
|
||||
<!-- Folder/file name button -->
|
||||
<button
|
||||
@click="navigateTo"
|
||||
class="flex items-center gap-2 flex-1 min-w-0 px-2 py-1.5 rounded-lg text-sm font-medium transition-colors text-gray-600 hover:bg-gray-100 hover:text-gray-900"
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 7a2 2 0 012-2h4l2 2h8a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
class="w-4 h-4 shrink-0 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<!-- Folder icon for directories, document icon for files -->
|
||||
<svg
|
||||
v-if="folder.is_dir"
|
||||
class="w-4 h-4 shrink-0 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 7a2 2 0 012-2h4l2 2h8a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V7z" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
class="w-4 h-4 shrink-0 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span class="truncate">{{ folder.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Children: nested sub-folders (lazy loaded) -->
|
||||
<template v-if="expanded">
|
||||
<div v-if="loading" class="text-xs text-gray-400 py-1" :style="{ paddingLeft: `${(depth + 1) * 12 + 8}px` }">Loading…</div>
|
||||
<div
|
||||
v-else-if="loadError"
|
||||
class="text-xs text-red-500 cursor-pointer py-1"
|
||||
:style="{ paddingLeft: `${(depth + 1) * 12 + 8}px` }"
|
||||
@click="retry"
|
||||
>
|
||||
Failed to load — tap to retry
|
||||
</div>
|
||||
<div v-else-if="children.length === 0" class="text-xs text-gray-400 py-1" :style="{ paddingLeft: `${(depth + 1) * 12 + 8}px` }">Empty</div>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</template>
|
||||
<template #children="{ children }">
|
||||
<CloudFolderTreeItem
|
||||
v-for="child in children"
|
||||
:key="child.id"
|
||||
@@ -75,13 +37,13 @@
|
||||
:depth="depth + 1"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</TreeItem>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import * as api from '../../api/client.js'
|
||||
import TreeItem from '../ui/TreeItem.vue'
|
||||
|
||||
const props = defineProps({
|
||||
folder: { type: Object, required: true },
|
||||
@@ -91,38 +53,12 @@ const props = defineProps({
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const expanded = ref(false)
|
||||
const children = ref([])
|
||||
const loading = ref(false)
|
||||
const loadError = ref(false)
|
||||
const childrenLoaded = ref(false)
|
||||
|
||||
async function loadChildren() {
|
||||
loading.value = true
|
||||
loadError.value = false
|
||||
try {
|
||||
const data = await api.getCloudFolders(props.provider, props.folder.id)
|
||||
children.value = (data.items ?? []).filter(i => i.is_dir)
|
||||
childrenLoaded.value = true
|
||||
} catch {
|
||||
loadError.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
const data = await api.getCloudFolders(props.provider, props.folder.id)
|
||||
return (data.items ?? []).filter(i => i.is_dir)
|
||||
}
|
||||
|
||||
async function toggleExpand() {
|
||||
if (!expanded.value && !childrenLoaded.value) {
|
||||
await loadChildren()
|
||||
}
|
||||
expanded.value = !expanded.value
|
||||
}
|
||||
|
||||
async function retry() {
|
||||
await loadChildren()
|
||||
}
|
||||
|
||||
function navigateTo() {
|
||||
function navigate() {
|
||||
router.push(`/cloud/${props.provider}/${props.folder.id}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user