generated from corrad-software/corrad-af-2024
146 lines
6.5 KiB
Vue
146 lines
6.5 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
items: {
|
|
type: Array,
|
|
default: () => [],
|
|
required: true
|
|
},
|
|
currentPath: {
|
|
type: String,
|
|
default: '/'
|
|
},
|
|
level: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
parentPath: {
|
|
type: String,
|
|
default: ''
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['navigate']);
|
|
|
|
// Local state for expanded items
|
|
const expandedItems = ref({});
|
|
|
|
// Toggle item expansion
|
|
const toggleItem = (item) => {
|
|
expandedItems.value[item.id] = !expandedItems.value[item.id];
|
|
};
|
|
|
|
// Navigate to an item
|
|
const navigateToItem = (item) => {
|
|
let path;
|
|
|
|
// Build path based on item type
|
|
if (item.type === 'file') {
|
|
// For files, we navigate to the parent folder but select the file
|
|
path = item.parentPath;
|
|
} else {
|
|
// For folders, we navigate to the folder itself
|
|
path = item.path || `${props.parentPath}/${item.id}`;
|
|
}
|
|
|
|
emit('navigate', { path, item });
|
|
};
|
|
|
|
// Check if item is on the current path
|
|
const isOnCurrentPath = (item) => {
|
|
if (props.currentPath === '/') return item.parentPath === '/';
|
|
|
|
const fullItemPath = `${props.parentPath}/${item.id}`;
|
|
return props.currentPath.startsWith(fullItemPath) || props.currentPath === fullItemPath;
|
|
};
|
|
|
|
// Get icon for item type
|
|
const getItemIcon = (item) => {
|
|
switch (item.type) {
|
|
case 'cabinet':
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/></svg>';
|
|
case 'drawer':
|
|
case 'folder':
|
|
case 'subfolder':
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>';
|
|
case 'file':
|
|
if (item.extension === 'pdf')
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><path d="M9 15h6"></path><path d="M9 11h6"></path></svg>';
|
|
if (['doc', 'docx'].includes(item.extension))
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>';
|
|
if (['xls', 'xlsx'].includes(item.extension))
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><rect x="8" y="12" width="8" height="6"></rect><line x1="8" y1="16" x2="16" y2="16"></line><line x1="11" y1="12" x2="11" y2="18"></line></svg>';
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>';
|
|
default:
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>';
|
|
}
|
|
};
|
|
|
|
// Filter child items for the current parent
|
|
const childItems = computed(() => {
|
|
return props.items.filter(item => {
|
|
// Root level items
|
|
if (props.level === 0 && props.parentPath === '') {
|
|
return item.parentPath === '/';
|
|
}
|
|
|
|
// Other level items
|
|
return item.parentPath === props.parentPath ||
|
|
(props.level === 1 && item.parentPath === `/${props.items[0]?.id}`);
|
|
});
|
|
});
|
|
|
|
// Get the next level items for a parent
|
|
const getChildrenForParent = (parentItem) => {
|
|
const parentPath = parentItem.path || `${props.parentPath}/${parentItem.id}`;
|
|
return props.items.filter(item => item.parentPath === parentPath);
|
|
};
|
|
|
|
// Check if an item has children
|
|
const hasChildren = (item) => {
|
|
const parentPath = item.path || `${props.parentPath}/${item.id}`;
|
|
return props.items.some(i => i.parentPath === parentPath);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="dms-tree-view" :style="{ paddingLeft: level > 0 ? '16px' : '0' }">
|
|
<div
|
|
v-for="item in childItems"
|
|
:key="item.id"
|
|
class="tree-item"
|
|
>
|
|
<div
|
|
@click="hasChildren(item) ? toggleItem(item) : navigateToItem(item)"
|
|
class="flex items-center gap-1 p-1 rounded cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
:class="isOnCurrentPath(item) ? 'bg-primary/20 text-primary' : ''"
|
|
>
|
|
<span v-if="hasChildren(item)" class="w-5">
|
|
<svg v-if="expandedItems[item.id]" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
|
|
<svg v-else xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
|
|
</span>
|
|
<span v-else class="w-5"></span> <!-- Spacer -->
|
|
|
|
<span class="w-5" v-html="getItemIcon(item)"></span>
|
|
<span class="truncate">{{ item.name }}</span>
|
|
</div>
|
|
|
|
<!-- Recursive component for nested levels -->
|
|
<DMSTreeView
|
|
v-if="expandedItems[item.id] && hasChildren(item)"
|
|
:items="props.items"
|
|
:current-path="props.currentPath"
|
|
:level="props.level + 1"
|
|
:parent-path="item.path || `${props.parentPath}/${item.id}`"
|
|
@navigate="(data) => emit('navigate', data)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.tree-item {
|
|
margin-bottom: 2px;
|
|
}
|
|
</style> |