first-update
This commit is contained in:
363
frontend/src/views/ProjectView.vue
Normal file
363
frontend/src/views/ProjectView.vue
Normal file
@@ -0,0 +1,363 @@
|
||||
<template>
|
||||
<div class="project-layout">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar" :class="{ collapsed: sidebarCollapsed }">
|
||||
<div class="sidebar-header">
|
||||
<div class="project-info" @click="goHome">
|
||||
<div class="back-btn">
|
||||
<el-icon><ArrowLeft /></el-icon>
|
||||
</div>
|
||||
<div class="project-details">
|
||||
<h2 class="project-name">{{ project.name }}</h2>
|
||||
<p class="project-desc">{{ project.description || '数据生成项目' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="collapse-btn" @click="sidebarCollapsed = !sidebarCollapsed">
|
||||
<el-icon><DArrowLeft v-if="!sidebarCollapsed" /><DArrowRight v-else /></el-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<router-link
|
||||
v-for="item in navItems"
|
||||
:key="item.path"
|
||||
:to="`/project/${id}/${item.path}`"
|
||||
class="nav-item"
|
||||
:class="{ active: isActive(item.path) }"
|
||||
>
|
||||
<div class="nav-icon">
|
||||
<el-icon size="20"><component :is="item.icon" /></el-icon>
|
||||
</div>
|
||||
<span class="nav-label">{{ item.label }}</span>
|
||||
<span class="nav-dot"></span>
|
||||
</router-link>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<router-link to="/" class="home-link">
|
||||
<el-icon><HomeFilled /></el-icon>
|
||||
<span>返回首页</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { projectApi } from '@/api'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const id = computed(() => route.params.id)
|
||||
const sidebarCollapsed = ref(false)
|
||||
|
||||
const project = ref({ name: '加载中...', description: '' })
|
||||
|
||||
const navItems = [
|
||||
{ path: 'files', label: '文件管理', icon: 'Folder' },
|
||||
{ path: 'split', label: '文本分割', icon: 'Operation' },
|
||||
{ path: 'questions', label: '问答管理', icon: 'ChatDotSquare' },
|
||||
{ path: 'datasets', label: '数据集', icon: 'Collection' },
|
||||
{ path: 'eval', label: '评估系统', icon: 'DataAnalysis' },
|
||||
{ path: 'settings', label: '项目设置', icon: 'Setting' }
|
||||
]
|
||||
|
||||
const isActive = (path) => route.path.includes(path)
|
||||
|
||||
const fetchProject = async () => {
|
||||
try {
|
||||
const res = await projectApi.get(id.value)
|
||||
project.value = res.data
|
||||
} catch (error) {
|
||||
ElMessage.error('加载项目失败')
|
||||
}
|
||||
}
|
||||
|
||||
const goHome = () => router.push('/')
|
||||
|
||||
onMounted(() => fetchProject())
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.project-layout {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ========================
|
||||
Sidebar
|
||||
======================== */
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
background: var(--bg-secondary);
|
||||
border-right: 1px solid var(--border-subtle);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.sidebar.collapsed {
|
||||
width: 72px;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
top: 28px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: 50%;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.collapse-btn:hover {
|
||||
background: var(--accent-primary-muted);
|
||||
color: var(--accent-primary);
|
||||
border-color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.project-info {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-secondary);
|
||||
flex-shrink: 0;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.project-info:hover .back-btn {
|
||||
background: var(--accent-primary-muted);
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.project-details {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .project-details {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.project-name {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 4px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.project-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.sidebar-nav {
|
||||
flex: 1;
|
||||
padding: 16px 12px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
margin-bottom: 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--accent-primary-muted);
|
||||
opacity: 0;
|
||||
transition: opacity var(--transition-fast);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-item:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: var(--accent-primary);
|
||||
}
|
||||
|
||||
.nav-item.active::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
white-space: nowrap;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-label {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nav-dot {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent-primary);
|
||||
opacity: 0;
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
.sidebar.collapsed .nav-dot {
|
||||
right: 50%;
|
||||
transform: translateX(50%);
|
||||
}
|
||||
|
||||
.nav-item.active .nav-dot {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Sidebar Footer */
|
||||
.sidebar-footer {
|
||||
padding: 16px 20px;
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.home-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-tertiary);
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.home-link:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.home-link span {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar.collapsed .home-link span {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
margin-left: 280px;
|
||||
min-height: 100vh;
|
||||
transition: margin-left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.sidebar.collapsed + .main-content,
|
||||
.sidebar.collapsed ~ .main-content {
|
||||
margin-left: 72px;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
width: 72px;
|
||||
}
|
||||
|
||||
.sidebar .project-details,
|
||||
.sidebar .nav-label,
|
||||
.sidebar .home-link span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 72px;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user