Files
3Dviewer/frontend/src/components/layout/AppLayout.vue
likegears 7af9c323f6 Initial commit: 3D Viewer application
Features:
- Vue 3 frontend with Three.js/Online3DViewer
- Node.js API with PostgreSQL and Redis
- Python worker for model conversion
- Docker Compose for deployment
- ViewCube navigation with drag rotation and 90° snap
- Cross-section, exploded view, and render settings
- Parts tree with visibility controls

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 14:00:17 +08:00

103 lines
2.6 KiB
Vue

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import SidebarPanel from './SidebarPanel.vue'
import ViewerPanel from './ViewerPanel.vue'
import PartsTreePanel from '@/components/partsTree/PartsTreePanel.vue'
import { usePartsTreeStore } from '@/stores/partsTree'
import { useViewerStore } from '@/stores/viewer'
const partsTreeStore = usePartsTreeStore()
const viewerStore = useViewerStore()
const isTreePanelCollapsed = ref(false)
const treePanelWidth = ref(280)
// Build tree when model loads - must be here so it runs before conditional render
watch(
() => viewerStore.model,
(model) => {
if (model && viewerStore.scene) {
partsTreeStore.buildTree()
} else {
partsTreeStore.reset()
}
},
{ immediate: true }
)
const treePanelStyle = computed(() => ({
width: isTreePanelCollapsed.value ? '0px' : `${treePanelWidth.value}px`,
minWidth: isTreePanelCollapsed.value ? '0px' : `${treePanelWidth.value}px`,
}))
function toggleTreePanel() {
isTreePanelCollapsed.value = !isTreePanelCollapsed.value
}
</script>
<template>
<div class="app-layout">
<SidebarPanel />
<ViewerPanel />
<div
v-if="partsTreeStore.hasTree"
class="tree-panel-wrapper"
:style="treePanelStyle"
>
<button
class="tree-panel-toggle"
:class="{ collapsed: isTreePanelCollapsed }"
@click="toggleTreePanel"
:title="isTreePanelCollapsed ? 'Show Parts Tree' : 'Hide Parts Tree'"
>
<svg viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
</svg>
</button>
<PartsTreePanel v-show="!isTreePanelCollapsed" />
</div>
</div>
</template>
<style scoped>
.tree-panel-wrapper {
position: relative;
flex-shrink: 0 !important;
transition: width 0.2s, min-width 0.2s;
/* Remove overflow: hidden to allow toggle button to be visible */
}
.tree-panel-toggle {
position: absolute;
left: -1.5rem;
top: 50%;
transform: translateY(-50%);
width: 1.5rem;
height: 3rem;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-right: none;
border-radius: 0.375rem 0 0 0.375rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.tree-panel-toggle:hover {
background: var(--bg-tertiary);
}
.tree-panel-toggle svg {
width: 1rem;
height: 1rem;
color: var(--text-secondary);
transition: transform 0.2s;
}
.tree-panel-toggle.collapsed svg {
transform: rotate(180deg);
}
</style>