fix: fix storage file
This commit is contained in:
4
bo/components.d.ts
vendored
4
bo/components.d.ts
vendored
@@ -13,7 +13,9 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ButtonGoToProfile: typeof import('./src/components/customer-management/ButtonGoToProfile.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_TermsAndConditionsView: typeof import('./src/components/terms/cs_TermsAndConditionsView.vue')['default']
|
||||
En_PrivacyPolicyView: typeof import('./src/components/terms/en_PrivacyPolicyView.vue')['default']
|
||||
|
||||
@@ -1,27 +1,37 @@
|
||||
<template>
|
||||
<component :is="Default || 'div'">
|
||||
<div class="p-4">
|
||||
|
||||
<div v-if="loading" class="flex justify-center py-8">
|
||||
<ULoader />
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="text-red-500">
|
||||
{{ error }}
|
||||
</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 }">
|
||||
<div class="flex items-start cursor-pointer" @click="onItemClick(item)">
|
||||
<div class="flex items-start cursor-pointer">
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<UIcon :name="item.icon" :size="30" />
|
||||
|
||||
<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"
|
||||
variant="outline" icon="i-lucide-download"
|
||||
@click.stop="downloadFile(item.path, item.fileName)" :ui="{ base: 'ring-0!' }" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</UTree>
|
||||
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
@@ -54,106 +64,106 @@ interface TreeItem {
|
||||
const props = defineProps<{ initialPath?: string }>()
|
||||
|
||||
const currentPath = ref(props.initialPath || '')
|
||||
const allData = ref<Map<string, FileItem[]>>(new Map())
|
||||
|
||||
const allData = ref<Record<string, FileItem[]>>({})
|
||||
const expandedFolders = ref<string[]>([])
|
||||
const treeKey = ref(0)
|
||||
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const showTree = computed(() => !error.value)
|
||||
|
||||
async function fetchFolderContents(path: string): Promise<FileItem[]> {
|
||||
const url = `/api/v1/restricted/storage/list-content/${path}`
|
||||
const data = await useFetchJson<FileItemRaw[]>(url)
|
||||
|
||||
return (data.items || []).map(i => ({
|
||||
name: i.Name,
|
||||
type: i.IsFolder ? 'folder' : 'file'
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
async function loadFolder(path: string) {
|
||||
if (allData.value.has(path)) return
|
||||
loading.value = true
|
||||
error.value = null
|
||||
if (allData.value[path]) return
|
||||
|
||||
try {
|
||||
const items = await fetchFolderContents(path)
|
||||
allData.value.set(path, items)
|
||||
allData.value[path] = items
|
||||
} catch (e) {
|
||||
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 => {
|
||||
const itemPath = path ? `${path}/${item.name}` : item.name
|
||||
const isFolder = item.type === 'folder'
|
||||
|
||||
const children = isFolder
|
||||
? buildTreeItems(allData.value.get(itemPath) || [], itemPath)
|
||||
: undefined
|
||||
const isExpanded = expandedFolders.value.includes(itemPath)
|
||||
const isLoaded = !!allData.value[itemPath]
|
||||
|
||||
return {
|
||||
label: item.name,
|
||||
|
||||
icon: isFolder ? 'fxemoji:folder' : 'flat-color-icons:file',
|
||||
isFolder,
|
||||
path: isFolder ? itemPath : path,
|
||||
value: itemPath,
|
||||
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) {
|
||||
try {
|
||||
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 url = window.URL.createObjectURL(blob)
|
||||
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = fileName
|
||||
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
a.remove()
|
||||
|
||||
window.URL.revokeObjectURL(url)
|
||||
} catch (e) {
|
||||
console.error('Download error:', e)
|
||||
alert('Failed to download file')
|
||||
console.error(e)
|
||||
alert('Download failed')
|
||||
}
|
||||
}
|
||||
|
||||
const treeItems = computed<TreeItem[]>(() => {
|
||||
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])
|
||||
}
|
||||
})
|
||||
loadFolder(currentPath.value)
|
||||
</script>
|
||||
Reference in New Issue
Block a user