fix: fix storage file

This commit is contained in:
2026-04-10 11:38:36 +02:00
parent bfd20aaa7b
commit f1a2f4c0b2
2 changed files with 62 additions and 50 deletions

4
bo/components.d.ts vendored
View File

@@ -13,7 +13,9 @@ declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
ButtonGoToProfile: typeof import('./src/components/customer-management/ButtonGoToProfile.vue')['default'] ButtonGoToProfile: typeof import('./src/components/customer-management/ButtonGoToProfile.vue')['default']
CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default'] CartDetails: typeof import('./src/components/customer/CartDetails.vue')['default']
CategoryMenu: typeof import('./src/components/inner/categoryMenu.vue')['default'] CategoryMenu: typeof import('./src/components/inner/CategoryMenu.vue')['default']
copy: typeof import('./src/components/admin/ProductDetailView copy.vue')['default']
CountryCurrencySwitch: typeof import('./src/components/inner/CountryCurrencySwitch.vue')['default']
Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default'] Cs_PrivacyPolicyView: typeof import('./src/components/terms/cs_PrivacyPolicyView.vue')['default']
Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default'] Cs_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default']
En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default'] En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default']

View File

@@ -1,27 +1,37 @@
<template> <template>
<component :is="Default || 'div'"> <component :is="Default || 'div'">
<div class="p-4"> <div class="p-4">
<div v-if="loading" class="flex justify-center py-8"> <div v-if="loading" class="flex justify-center py-8">
<ULoader /> <ULoader />
</div> </div>
<div v-else-if="error" class="text-red-500"> <div v-else-if="error" class="text-red-500">
{{ error }} {{ error }}
</div> </div>
<UTree v-else :items="treeItems" :expanded="expandedFolders">
<UTree v-if="showTree" :items="treeItems" v-model:expanded="expandedFolders" :key="treeKey" @toggle="onToggle" :get-key="item => item.value">
<template #item-wrapper="{ item }"> <template #item-wrapper="{ item }">
<div class="flex items-start cursor-pointer" @click="onItemClick(item)"> <div class="flex items-start cursor-pointer">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<UIcon :name="item.icon" :size="30" /> <UIcon :name="item.icon" :size="30" />
<div class="flex gap-1 items-center"> <div class="flex gap-1 items-center">
<span class="text-[15px] font-medium">{{ item.label }}</span> <span class="text-[15px] font-medium">
{{ item.label }}
</span>
<UButton v-if="!item.isFolder && item.fileName" size="xxs" color="neutral" <UButton v-if="!item.isFolder && item.fileName" size="xxs" color="neutral"
variant="outline" icon="i-lucide-download" variant="outline" icon="i-lucide-download"
@click.stop="downloadFile(item.path, item.fileName)" :ui="{ base: 'ring-0!' }" /> @click.stop="downloadFile(item.path, item.fileName)" :ui="{ base: 'ring-0!' }" />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
</UTree> </UTree>
</div> </div>
</component> </component>
</template> </template>
@@ -54,106 +64,106 @@ interface TreeItem {
const props = defineProps<{ initialPath?: string }>() const props = defineProps<{ initialPath?: string }>()
const currentPath = ref(props.initialPath || '') const currentPath = ref(props.initialPath || '')
const allData = ref<Map<string, FileItem[]>>(new Map())
const allData = ref<Record<string, FileItem[]>>({})
const expandedFolders = ref<string[]>([]) const expandedFolders = ref<string[]>([])
const treeKey = ref(0)
const loading = ref(false) const loading = ref(false)
const error = ref<string | null>(null) const error = ref<string | null>(null)
const showTree = computed(() => !error.value)
async function fetchFolderContents(path: string): Promise<FileItem[]> { async function fetchFolderContents(path: string): Promise<FileItem[]> {
const url = `/api/v1/restricted/storage/list-content/${path}` const url = `/api/v1/restricted/storage/list-content/${path}`
const data = await useFetchJson<FileItemRaw[]>(url) const data = await useFetchJson<FileItemRaw[]>(url)
return (data.items || []).map(i => ({ return (data.items || []).map(i => ({
name: i.Name, name: i.Name,
type: i.IsFolder ? 'folder' : 'file' type: i.IsFolder ? 'folder' : 'file'
})) }))
} }
async function loadFolder(path: string) { async function loadFolder(path: string) {
if (allData.value.has(path)) return if (allData.value[path]) return
loading.value = true
error.value = null
try { try {
const items = await fetchFolderContents(path) const items = await fetchFolderContents(path)
allData.value.set(path, items) allData.value[path] = items
} catch (e) { } catch (e) {
error.value = e instanceof Error ? e.message : 'Failed to load folder contents' error.value = e instanceof Error ? e.message : 'Failed to load folder contents'
} finally {
loading.value = false
} }
} }
function buildTree(path: string): TreeItem[] {
const items = allData.value[path] || []
async function toggleFolder(item: TreeItem) {
if (!item.isFolder) return
if (!expandedFolders.value.includes(item.value)) {
expandedFolders.value.push(item.value)
await loadFolder(item.value)
} else {
expandedFolders.value = expandedFolders.value.filter(v => v !== item.value)
}
}
function onItemClick(item: TreeItem) {
if (item.isFolder) {
toggleFolder(item)
}
}
function buildTreeItems(items: FileItem[], path: string): TreeItem[] {
return items.map(item => { return items.map(item => {
const itemPath = path ? `${path}/${item.name}` : item.name const itemPath = path ? `${path}/${item.name}` : item.name
const isFolder = item.type === 'folder' const isFolder = item.type === 'folder'
const children = isFolder const isExpanded = expandedFolders.value.includes(itemPath)
? buildTreeItems(allData.value.get(itemPath) || [], itemPath) const isLoaded = !!allData.value[itemPath]
: undefined
return { return {
label: item.name, label: item.name,
icon: isFolder ? 'fxemoji:folder' : 'flat-color-icons:file', icon: isFolder ? 'fxemoji:folder' : 'flat-color-icons:file',
isFolder, isFolder,
path: isFolder ? itemPath : path, path: isFolder ? itemPath : path,
value: itemPath, value: itemPath,
fileName: isFolder ? undefined : item.name, fileName: isFolder ? undefined : item.name,
children
children: isFolder && isExpanded && isLoaded
? buildTree(itemPath)
: []
} }
}) })
} }
const treeItems = computed(() => buildTree(currentPath.value))
async function toggleFolder(item: TreeItem) {
if (!item.isFolder) return
const isOpen = expandedFolders.value.includes(item.value)
if (!isOpen) {
await loadFolder(item.value)
treeKey.value++
} else {
treeKey.value++
}
}
function onToggle(_event: unknown, item: TreeItem) {
console.log('Toggle:', item)
if (item.isFolder) {
toggleFolder(item)
}
}
async function downloadFile(path: string, fileName: string) { async function downloadFile(path: string, fileName: string) {
try { try {
const response = await fetch(`/api/v1/restricted/storage/download-file/${path}/${fileName}`) const response = await fetch(`/api/v1/restricted/storage/download-file/${path}/${fileName}`)
if (!response.ok) throw new Error('Failed to download file') if (!response.ok) throw new Error('Download failed')
const blob = await response.blob() const blob = await response.blob()
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
const a = document.createElement('a') const a = document.createElement('a')
a.href = url a.href = url
a.download = fileName a.download = fileName
document.body.appendChild(a) document.body.appendChild(a)
a.click() a.click()
a.remove() a.remove()
window.URL.revokeObjectURL(url) window.URL.revokeObjectURL(url)
} catch (e) { } catch (e) {
console.error('Download error:', e) console.error(e)
alert('Failed to download file') alert('Download failed')
} }
} }
const treeItems = computed<TreeItem[]>(() => { loadFolder(currentPath.value)
const items = allData.value.get(currentPath.value) || []
return buildTreeItems(items, currentPath.value)
})
loadFolder(currentPath.value).then(() => {
const rootItems = treeItems.value
if (rootItems.length > 0 && rootItems[0].isFolder) {
toggleFolder(rootItems[0])
}
})
</script> </script>