2026-03-17 14:36:31 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="home">
|
|
|
|
|
|
<!-- Hero Section -->
|
|
|
|
|
|
<section class="hero">
|
|
|
|
|
|
<div class="hero-content">
|
|
|
|
|
|
<div class="hero-badge">
|
|
|
|
|
|
<span class="badge-dot"></span>
|
|
|
|
|
|
<span>AI 驱动数据生成</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<h1 class="hero-title">
|
|
|
|
|
|
构建高质量<br />
|
|
|
|
|
|
<span class="glow-text">训练数据集</span>
|
|
|
|
|
|
</h1>
|
|
|
|
|
|
<p class="hero-subtitle">
|
|
|
|
|
|
通过智能分割、AI 生成问答和无缝评估,
|
|
|
|
|
|
将文档转化为结构化数据集。
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div class="hero-actions">
|
|
|
|
|
|
<el-button type="primary" size="large" @click="createProject" class="btn-primary">
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
创建项目
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button size="large" @click="goToDataSquare" class="btn-secondary">
|
|
|
|
|
|
<el-icon><Grid /></el-icon>
|
|
|
|
|
|
数据集广场
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Hero Visual - 全息粒子矩阵风格 -->
|
|
|
|
|
|
<div class="hero-visual">
|
|
|
|
|
|
<!-- Card 1: 多格式支持 -->
|
|
|
|
|
|
<div class="hologram-card card-1">
|
|
|
|
|
|
<div class="card-bg"></div>
|
|
|
|
|
|
<div class="scan-line"></div>
|
|
|
|
|
|
<div class="particles-container">
|
|
|
|
|
|
<span class="particle" style="--x: 20%; --y: 30%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 80%; --y: 20%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 50%; --y: 70%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 30%; --y: 60%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 70%; --y: 80%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 15%; --y: 85%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 85%; --y: 45%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 45%; --y: 15%"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="pulse-ring"></div>
|
|
|
|
|
|
<div class="card-content">
|
|
|
|
|
|
<div class="icon-wrapper cyan">
|
|
|
|
|
|
<div class="icon-glow"></div>
|
|
|
|
|
|
<el-icon size="28"><Document /></el-icon>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="card-label">多格式支持</span>
|
|
|
|
|
|
<span class="card-sublabel">PDF DOCX EPUB Excel</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Card 2: AI 生成 -->
|
|
|
|
|
|
<div class="hologram-card card-2">
|
|
|
|
|
|
<div class="card-bg"></div>
|
|
|
|
|
|
<div class="scan-line"></div>
|
|
|
|
|
|
<div class="particles-container">
|
|
|
|
|
|
<span class="particle" style="--x: 25%; --y: 35%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 75%; --y: 25%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 55%; --y: 65%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 35%; --y: 55%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 65%; --y: 85%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 20%; --y: 80%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 80%; --y: 50%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 50%; --y: 20%"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="pulse-ring"></div>
|
|
|
|
|
|
<div class="card-content">
|
|
|
|
|
|
<div class="icon-wrapper violet">
|
|
|
|
|
|
<div class="icon-glow"></div>
|
|
|
|
|
|
<el-icon size="28"><MagicStick /></el-icon>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="card-label">AI 生成</span>
|
|
|
|
|
|
<span class="card-sublabel">智能问答 自动标注</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Card 3: 智能评估 -->
|
|
|
|
|
|
<div class="hologram-card card-3">
|
|
|
|
|
|
<div class="card-bg"></div>
|
|
|
|
|
|
<div class="scan-line"></div>
|
|
|
|
|
|
<div class="particles-container">
|
|
|
|
|
|
<span class="particle" style="--x: 30%; --y: 25%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 70%; --y: 35%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 45%; --y: 75%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 25%; --y: 65%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 75%; --y: 85%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 10%; --y: 75%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 90%; --y: 40%"></span>
|
|
|
|
|
|
<span class="particle" style="--x: 40%; --y: 10%"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="pulse-ring"></div>
|
|
|
|
|
|
<div class="card-content">
|
|
|
|
|
|
<div class="icon-wrapper teal">
|
|
|
|
|
|
<div class="icon-glow"></div>
|
|
|
|
|
|
<el-icon size="28"><DataAnalysis /></el-icon>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="card-label">智能评估</span>
|
|
|
|
|
|
<span class="card-sublabel">质量分析 模型对比</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Quick Actions -->
|
|
|
|
|
|
<section class="quick-actions">
|
2026-03-17 17:29:11 +08:00
|
|
|
|
<div class="action-card" @click="goToModels">
|
2026-03-17 14:36:31 +08:00
|
|
|
|
<div class="action-icon">
|
2026-03-17 17:29:11 +08:00
|
|
|
|
<el-icon><Setting /></el-icon>
|
2026-03-17 14:36:31 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="action-info">
|
2026-03-17 17:29:11 +08:00
|
|
|
|
<h3>模型配置</h3>
|
|
|
|
|
|
<p>管理 AI 模型 API 配置</p>
|
2026-03-17 14:36:31 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<el-icon class="action-arrow"><ArrowRight /></el-icon>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Projects Section -->
|
|
|
|
|
|
<section class="projects-section">
|
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
|
<div class="section-title">
|
|
|
|
|
|
<h2>我的项目</h2>
|
|
|
|
|
|
<p>{{ projects.length }} 个项目</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-button type="primary" @click="createProject" class="add-btn">
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
新建
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Projects Grid -->
|
|
|
|
|
|
<div class="projects-grid" v-loading="loading">
|
|
|
|
|
|
<!-- Empty State -->
|
2026-03-17 17:29:11 +08:00
|
|
|
|
<EmptyState
|
|
|
|
|
|
v-if="!loading && projects.length === 0"
|
|
|
|
|
|
:icon="FolderAdd"
|
|
|
|
|
|
title="暂无项目"
|
|
|
|
|
|
description="创建您的第一个项目开始生成数据集"
|
|
|
|
|
|
action-text="创建项目"
|
|
|
|
|
|
@action="createProject"
|
|
|
|
|
|
/>
|
2026-03-17 14:36:31 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- Project Cards -->
|
2026-03-17 17:29:11 +08:00
|
|
|
|
<ProjectCard
|
2026-03-17 14:36:31 +08:00
|
|
|
|
v-else
|
|
|
|
|
|
v-for="(project, index) in projects"
|
|
|
|
|
|
:key="project.id"
|
2026-03-17 17:29:11 +08:00
|
|
|
|
:project="project"
|
|
|
|
|
|
:index="index"
|
|
|
|
|
|
@click="openProject"
|
|
|
|
|
|
@delete="confirmDelete"
|
|
|
|
|
|
/>
|
2026-03-17 14:36:31 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Create Dialog -->
|
2026-03-17 17:29:11 +08:00
|
|
|
|
<CreateProjectDialog
|
|
|
|
|
|
v-model:visible="dialogVisible"
|
|
|
|
|
|
:loading="submitting"
|
|
|
|
|
|
@submit="handleCreateSubmit"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Delete Confirmation Dialog -->
|
|
|
|
|
|
<DeleteDialog
|
|
|
|
|
|
v-model:visible="deleteDialogVisible"
|
|
|
|
|
|
:item-name="projectToDelete?.name"
|
|
|
|
|
|
:loading="deleting"
|
|
|
|
|
|
@confirm="handleDelete"
|
|
|
|
|
|
/>
|
2026-03-17 14:36:31 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2026-03-17 17:29:11 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ref, onMounted } from 'vue'
|
2026-03-17 14:36:31 +08:00
|
|
|
|
import { useRouter } from 'vue-router'
|
2026-03-17 17:29:11 +08:00
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
|
import { FolderAdd } from '@element-plus/icons-vue'
|
2026-03-17 14:36:31 +08:00
|
|
|
|
import { projectApi } from '@/api'
|
2026-03-17 17:29:11 +08:00
|
|
|
|
import type { Project, ProjectCreate } from '@/types'
|
|
|
|
|
|
|
|
|
|
|
|
// Components
|
|
|
|
|
|
import EmptyState from '@/components/common/EmptyState.vue'
|
|
|
|
|
|
import ProjectCard from '@/components/common/ProjectCard.vue'
|
|
|
|
|
|
import CreateProjectDialog from '@/components/common/CreateProjectDialog.vue'
|
|
|
|
|
|
import DeleteDialog from '@/components/common/DeleteDialog.vue'
|
2026-03-17 14:36:31 +08:00
|
|
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const projects = ref([])
|
|
|
|
|
|
const dialogVisible = ref(false)
|
2026-03-17 17:29:11 +08:00
|
|
|
|
const deleteDialogVisible = ref(false)
|
|
|
|
|
|
const projectToDelete = ref(null)
|
2026-03-17 14:36:31 +08:00
|
|
|
|
const submitting = ref(false)
|
2026-03-17 17:29:11 +08:00
|
|
|
|
const deleting = ref(false)
|
2026-03-17 14:36:31 +08:00
|
|
|
|
|
|
|
|
|
|
const fetchProjects = async () => {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await projectApi.list()
|
2026-03-17 17:29:11 +08:00
|
|
|
|
// New paginated format: {items: [...], total, page, page_size}
|
|
|
|
|
|
projects.value = res.items || res || []
|
2026-03-17 14:36:31 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
projects.value = []
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const createProject = () => {
|
|
|
|
|
|
dialogVisible.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-17 17:29:11 +08:00
|
|
|
|
const handleCreateSubmit = async (formData) => {
|
|
|
|
|
|
// Simple validation
|
|
|
|
|
|
if (!formData.name || formData.name.trim() === '') {
|
|
|
|
|
|
ElMessage.warning('请输入项目名称')
|
|
|
|
|
|
return
|
2026-03-17 14:36:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-17 17:29:11 +08:00
|
|
|
|
console.log('Creating project with form:', formData)
|
2026-03-17 14:36:31 +08:00
|
|
|
|
submitting.value = true
|
|
|
|
|
|
try {
|
2026-03-17 17:29:11 +08:00
|
|
|
|
const res = await projectApi.create(formData)
|
|
|
|
|
|
console.log('Create response:', res)
|
2026-03-17 14:36:31 +08:00
|
|
|
|
ElMessage.success('项目创建成功')
|
|
|
|
|
|
dialogVisible.value = false
|
|
|
|
|
|
fetchProjects()
|
2026-03-17 17:29:11 +08:00
|
|
|
|
// New format: {id: "..."}
|
|
|
|
|
|
const projectId = res.id
|
|
|
|
|
|
console.log('Navigating to:', projectId)
|
|
|
|
|
|
router.push(`/project/${projectId}`)
|
2026-03-17 14:36:31 +08:00
|
|
|
|
} catch (error) {
|
2026-03-17 17:29:11 +08:00
|
|
|
|
console.error('Create project error:', error)
|
|
|
|
|
|
ElMessage.error('创建项目失败: ' + (error.message || '未知错误'))
|
2026-03-17 14:36:31 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
submitting.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const openProject = (project) => {
|
|
|
|
|
|
router.push(`/project/${project.id}`)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-17 17:29:11 +08:00
|
|
|
|
const confirmDelete = (project) => {
|
|
|
|
|
|
projectToDelete.value = project
|
|
|
|
|
|
deleteDialogVisible.value = true
|
2026-03-17 14:36:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-17 17:29:11 +08:00
|
|
|
|
const handleDelete = async () => {
|
|
|
|
|
|
if (!projectToDelete.value) return
|
|
|
|
|
|
deleting.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
await projectApi.delete(projectToDelete.value.id)
|
|
|
|
|
|
ElMessage.success('项目已删除')
|
|
|
|
|
|
deleteDialogVisible.value = false
|
|
|
|
|
|
projectToDelete.value = null
|
|
|
|
|
|
fetchProjects()
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
ElMessage.error('删除失败')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
deleting.value = false
|
|
|
|
|
|
}
|
2026-03-17 14:36:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const goToDataSquare = () => router.push('/data-square')
|
2026-03-17 17:29:11 +08:00
|
|
|
|
const goToModels = () => router.push('/models')
|
2026-03-17 14:36:31 +08:00
|
|
|
|
|
|
|
|
|
|
onMounted(() => fetchProjects())
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2026-03-17 17:29:11 +08:00
|
|
|
|
@import '@/styles/home.scss';
|
2026-03-17 14:36:31 +08:00
|
|
|
|
</style>
|