first-update

This commit is contained in:
2026-03-17 14:36:31 +08:00
parent 72f08aee7c
commit 4eddf05e79
516 changed files with 115270 additions and 1 deletions

View 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>