|
@@ -0,0 +1,1018 @@
|
|
|
+<template>
|
|
|
+ <div class="competition-selection-container">
|
|
|
+ <!-- 页面头部 -->
|
|
|
+ <PageHeader
|
|
|
+ title="比赛与项目遴选"
|
|
|
+ description="智能化比赛项目发布、学生报名与筛选匹配系统"
|
|
|
+ :icon="Trophy"
|
|
|
+ :breadcrumbs="breadcrumbs"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 功能导航 -->
|
|
|
+ <div class="function-nav">
|
|
|
+ <el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
|
|
+ <el-tab-pane label="项目发布" name="publish">
|
|
|
+ <div class="tab-content">
|
|
|
+ <!-- 发布项目区域 -->
|
|
|
+ <div class="publish-section">
|
|
|
+ <div class="section-header">
|
|
|
+ <h3>项目发布管理</h3>
|
|
|
+ <el-button type="primary" :icon="Plus" @click="showPublishDialog = true">
|
|
|
+ 发布新项目
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 项目列表 -->
|
|
|
+ <div class="project-list">
|
|
|
+ <div class="project-card" v-for="project in publishedProjects" :key="project.id">
|
|
|
+ <div class="project-header">
|
|
|
+ <div class="project-info">
|
|
|
+ <h4>{{ project.title }}</h4>
|
|
|
+ <el-tag :type="getProjectType(project.type)" size="small">{{ project.type }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="project-status">
|
|
|
+ <el-tag :type="getStatusType(project.status)">{{ project.status }}</el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="project-content">
|
|
|
+ <p class="project-description">{{ project.description }}</p>
|
|
|
+
|
|
|
+ <div class="project-requirements">
|
|
|
+ <span class="label">技能要求:</span>
|
|
|
+ <el-tag v-for="skill in project.requiredSkills" :key="skill" size="small" class="skill-tag">
|
|
|
+ {{ skill }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="project-details">
|
|
|
+ <div class="detail-item">
|
|
|
+ <el-icon><Calendar /></el-icon>
|
|
|
+ <span>报名截止: {{ project.deadline }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <el-icon><UserFilled /></el-icon>
|
|
|
+ <span>团队规模: {{ project.teamSize }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <el-icon><Trophy /></el-icon>
|
|
|
+ <span>奖励: {{ project.reward }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="project-stats">
|
|
|
+ <div class="stat-item">
|
|
|
+ <span class="stat-number">{{ project.applicants }}</span>
|
|
|
+ <span class="stat-label">报名人数</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <span class="stat-number">{{ project.matched }}</span>
|
|
|
+ <span class="stat-label">已匹配</span>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <span class="stat-number">{{ project.teams }}</span>
|
|
|
+ <span class="stat-label">组建团队</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="project-actions">
|
|
|
+ <el-button size="small" @click="viewProjectDetail(project)">详情</el-button>
|
|
|
+ <el-button size="small" type="primary" @click="manageApplicants(project)">管理报名</el-button>
|
|
|
+ <el-button size="small" type="warning" @click="editProject(project)">编辑</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <el-tab-pane label="学生报名" name="registration">
|
|
|
+ <div class="tab-content">
|
|
|
+ <!-- 报名统计 -->
|
|
|
+ <div class="registration-stats">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6" v-for="stat in registrationStats" :key="stat.label">
|
|
|
+ <div class="stat-card">
|
|
|
+ <div class="stat-icon" :style="{ background: stat.color }">
|
|
|
+ <el-icon>
|
|
|
+ <component :is="stat.icon" />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-number">{{ stat.value }}</div>
|
|
|
+ <div class="stat-label">{{ stat.label }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 报名列表 -->
|
|
|
+ <div class="registration-list">
|
|
|
+ <div class="section-header">
|
|
|
+ <h3>报名管理</h3>
|
|
|
+ <div class="header-filters">
|
|
|
+ <el-select v-model="filterProject" placeholder="筛选项目" style="width: 200px; margin-right: 12px;">
|
|
|
+ <el-option label="全部项目" value="" />
|
|
|
+ <el-option v-for="project in publishedProjects" :key="project.id" :label="project.title" :value="project.id" />
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="filterStatus" placeholder="筛选状态" style="width: 120px;">
|
|
|
+ <el-option label="全部状态" value="" />
|
|
|
+ <el-option label="待审核" value="pending" />
|
|
|
+ <el-option label="已通过" value="approved" />
|
|
|
+ <el-option label="已拒绝" value="rejected" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table :data="filteredRegistrations" style="width: 100%">
|
|
|
+ <el-table-column prop="studentName" label="学生姓名" width="120" />
|
|
|
+ <el-table-column prop="studentId" label="学号" width="120" />
|
|
|
+ <el-table-column prop="projectTitle" label="报名项目" width="200" />
|
|
|
+ <el-table-column prop="skills" label="技能标签" width="250">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag v-for="skill in scope.row.skills" :key="skill" size="small" class="skill-tag">
|
|
|
+ {{ skill }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="experience" label="相关经验" width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-rate v-model="scope.row.experience" disabled show-score />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="motivation" label="申请理由" width="200" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="status" label="状态" width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag :type="getRegistrationStatusType(scope.row.status)">
|
|
|
+ {{ getRegistrationStatusText(scope.row.status) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="applyTime" label="报名时间" width="120" />
|
|
|
+ <el-table-column label="操作" width="200">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button size="small" @click="viewStudentProfile(scope.row)">档案</el-button>
|
|
|
+ <el-button size="small" type="success" @click="approveRegistration(scope.row)" v-if="scope.row.status === 'pending'">
|
|
|
+ 通过
|
|
|
+ </el-button>
|
|
|
+ <el-button size="small" type="danger" @click="rejectRegistration(scope.row)" v-if="scope.row.status === 'pending'">
|
|
|
+ 拒绝
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <el-tab-pane label="智能匹配" name="matching">
|
|
|
+ <div class="tab-content">
|
|
|
+ <!-- 匹配配置 -->
|
|
|
+ <div class="matching-config">
|
|
|
+ <div class="section-header">
|
|
|
+ <h3>智能匹配配置</h3>
|
|
|
+ <el-button type="primary" :icon="Magic" @click="performMatching">
|
|
|
+ 执行智能匹配
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-form :model="matchingForm" label-width="120px" class="matching-form">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="目标项目">
|
|
|
+ <el-select v-model="matchingForm.projectId" placeholder="请选择项目">
|
|
|
+ <el-option v-for="project in publishedProjects" :key="project.id" :label="project.title" :value="project.id" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="匹配算法">
|
|
|
+ <el-select v-model="matchingForm.algorithm" placeholder="请选择算法">
|
|
|
+ <el-option label="技能匹配优先" value="skill-first" />
|
|
|
+ <el-option label="经验权重优先" value="experience-first" />
|
|
|
+ <el-option label="综合评估" value="comprehensive" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="团队规模">
|
|
|
+ <el-input-number v-model="matchingForm.teamSize" :min="2" :max="8" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-form-item label="权重配置">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="weight-item">
|
|
|
+ <span>技能匹配</span>
|
|
|
+ <el-slider v-model="matchingForm.weights.skill" :max="100" />
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="weight-item">
|
|
|
+ <span>经验水平</span>
|
|
|
+ <el-slider v-model="matchingForm.weights.experience" :max="100" />
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="weight-item">
|
|
|
+ <span>学习能力</span>
|
|
|
+ <el-slider v-model="matchingForm.weights.learning" :max="100" />
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="weight-item">
|
|
|
+ <span>团队协作</span>
|
|
|
+ <el-slider v-model="matchingForm.weights.teamwork" :max="100" />
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 匹配结果 -->
|
|
|
+ <div class="matching-results">
|
|
|
+ <h3>匹配结果</h3>
|
|
|
+ <div class="result-list">
|
|
|
+ <div class="result-card" v-for="result in matchingResults" :key="result.id">
|
|
|
+ <div class="result-header">
|
|
|
+ <h4>{{ result.projectTitle }} - 团队 {{ result.teamId }}</h4>
|
|
|
+ <div class="result-score">
|
|
|
+ <el-progress type="circle" :percentage="result.matchScore" :width="60" />
|
|
|
+ <span class="score-label">匹配度</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="result-content">
|
|
|
+ <div class="team-members">
|
|
|
+ <h5>团队成员</h5>
|
|
|
+ <div class="member-list">
|
|
|
+ <div class="member-item" v-for="member in result.members" :key="member.id">
|
|
|
+ <div class="member-avatar">
|
|
|
+ <el-icon><UserFilled /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="member-info">
|
|
|
+ <div class="member-name">{{ member.name }}</div>
|
|
|
+ <div class="member-skills">
|
|
|
+ <el-tag v-for="skill in member.skills.slice(0, 3)" :key="skill" size="small">
|
|
|
+ {{ skill }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="member-score">
|
|
|
+ <el-rate v-model="member.score" disabled show-score />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="match-analysis">
|
|
|
+ <h5>匹配分析</h5>
|
|
|
+ <div class="analysis-item">
|
|
|
+ <span class="analysis-label">技能覆盖率:</span>
|
|
|
+ <el-progress :percentage="result.analysis.skillCoverage" />
|
|
|
+ </div>
|
|
|
+ <div class="analysis-item">
|
|
|
+ <span class="analysis-label">经验互补性:</span>
|
|
|
+ <el-progress :percentage="result.analysis.experienceBalance" />
|
|
|
+ </div>
|
|
|
+ <div class="analysis-item">
|
|
|
+ <span class="analysis-label">团队协作度:</span>
|
|
|
+ <el-progress :percentage="result.analysis.teamworkScore" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="result-actions">
|
|
|
+ <el-button size="small" @click="viewDetailedAnalysis(result)">详细分析</el-button>
|
|
|
+ <el-button size="small" type="primary" @click="confirmTeamFormation(result)">确认组队</el-button>
|
|
|
+ <el-button size="small" type="warning" @click="adjustTeam(result)">调整团队</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 发布项目对话框 -->
|
|
|
+ <el-dialog v-model="showPublishDialog" title="发布新项目" width="700px">
|
|
|
+ <el-form :model="publishForm" label-width="120px">
|
|
|
+ <el-form-item label="项目标题">
|
|
|
+ <el-input v-model="publishForm.title" placeholder="请输入项目标题" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="项目类型">
|
|
|
+ <el-select v-model="publishForm.type" placeholder="请选择项目类型">
|
|
|
+ <el-option label="学科竞赛" value="学科竞赛" />
|
|
|
+ <el-option label="创新项目" value="创新项目" />
|
|
|
+ <el-option label="实践项目" value="实践项目" />
|
|
|
+ <el-option label="研究项目" value="研究项目" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="技能要求">
|
|
|
+ <el-select v-model="publishForm.requiredSkills" placeholder="请选择技能要求" multiple>
|
|
|
+ <el-option v-for="skill in availableSkills" :key="skill" :label="skill" :value="skill" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="团队规模">
|
|
|
+ <el-input-number v-model="publishForm.teamSize" :min="2" :max="8" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="报名截止">
|
|
|
+ <el-date-picker v-model="publishForm.deadline" type="date" placeholder="请选择截止时间" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="项目奖励">
|
|
|
+ <el-input v-model="publishForm.reward" placeholder="请输入项目奖励" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="项目描述">
|
|
|
+ <el-input v-model="publishForm.description" type="textarea" rows="4" placeholder="请输入项目描述" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="showPublishDialog = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="publishProject">发布</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive, computed } from 'vue'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import {
|
|
|
+ Trophy, Plus, Calendar, UserFilled, Magic,
|
|
|
+ TrendCharts, CheckCircle, Clock, Warning
|
|
|
+} from '@element-plus/icons-vue'
|
|
|
+import PageHeader from '@/components/PageHeader.vue'
|
|
|
+import StatCard from '@/components/StatCard.vue'
|
|
|
+import ActionCard from '@/components/ActionCard.vue'
|
|
|
+
|
|
|
+const router = useRouter()
|
|
|
+
|
|
|
+// 面包屑导航
|
|
|
+const breadcrumbs = [
|
|
|
+ { label: '首页', path: '/' },
|
|
|
+ { label: '工作室建设与管理', path: '/studio-mgmt' },
|
|
|
+ { label: '比赛与项目遴选', path: '/studio-mgmt/competition-selection' }
|
|
|
+]
|
|
|
+
|
|
|
+// 当前激活的标签页
|
|
|
+const activeTab = ref('publish')
|
|
|
+
|
|
|
+// 已发布项目数据
|
|
|
+const publishedProjects = ref([
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ title: '全国大学生数学建模竞赛',
|
|
|
+ type: '学科竞赛',
|
|
|
+ description: '运用数学方法解决实际问题,培养学生的创新思维和实践能力',
|
|
|
+ requiredSkills: ['数学建模', 'MATLAB', 'Python', '数据分析'],
|
|
|
+ teamSize: '3人',
|
|
|
+ deadline: '2024-03-15',
|
|
|
+ reward: '国家级证书 + 奖金',
|
|
|
+ status: '报名中',
|
|
|
+ applicants: 45,
|
|
|
+ matched: 36,
|
|
|
+ teams: 12
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ title: '智能家居控制系统开发',
|
|
|
+ type: '创新项目',
|
|
|
+ description: '基于物联网技术的智能家居控制系统设计与实现',
|
|
|
+ requiredSkills: ['物联网', 'Java', 'React', '硬件开发'],
|
|
|
+ teamSize: '4-5人',
|
|
|
+ deadline: '2024-02-28',
|
|
|
+ reward: '项目资助 + 专利申请',
|
|
|
+ status: '进行中',
|
|
|
+ applicants: 32,
|
|
|
+ matched: 28,
|
|
|
+ teams: 7
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 3,
|
|
|
+ title: '机器学习算法优化研究',
|
|
|
+ type: '研究项目',
|
|
|
+ description: '针对特定领域的机器学习算法优化与性能提升研究',
|
|
|
+ requiredSkills: ['机器学习', 'Python', 'TensorFlow', '算法优化'],
|
|
|
+ teamSize: '2-3人',
|
|
|
+ deadline: '2024-04-10',
|
|
|
+ reward: '学术论文发表机会',
|
|
|
+ status: '即将开始',
|
|
|
+ applicants: 28,
|
|
|
+ matched: 18,
|
|
|
+ teams: 6
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+// 报名统计数据
|
|
|
+const registrationStats = ref([
|
|
|
+ { label: '总报名数', value: 105, icon: 'UserFilled', color: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
|
|
|
+ { label: '待审核', value: 23, icon: 'Clock', color: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' },
|
|
|
+ { label: '已通过', value: 68, icon: 'CheckCircle', color: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' },
|
|
|
+ { label: '已匹配', value: 82, icon: 'TrendCharts', color: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)' }
|
|
|
+])
|
|
|
+
|
|
|
+// 报名列表数据
|
|
|
+const registrationList = ref([
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ studentName: '张三',
|
|
|
+ studentId: '2021001',
|
|
|
+ projectTitle: '全国大学生数学建模竞赛',
|
|
|
+ projectId: 1,
|
|
|
+ skills: ['数学建模', 'MATLAB', 'Python'],
|
|
|
+ experience: 4,
|
|
|
+ motivation: '对数学建模有浓厚兴趣,希望通过比赛提升实践能力',
|
|
|
+ status: 'pending',
|
|
|
+ applyTime: '2024-01-15'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ studentName: '李四',
|
|
|
+ studentId: '2021002',
|
|
|
+ projectTitle: '智能家居控制系统开发',
|
|
|
+ projectId: 2,
|
|
|
+ skills: ['Java', 'React', '物联网'],
|
|
|
+ experience: 3,
|
|
|
+ motivation: '有相关项目经验,希望深入学习物联网技术',
|
|
|
+ status: 'approved',
|
|
|
+ applyTime: '2024-01-12'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 3,
|
|
|
+ studentName: '王五',
|
|
|
+ studentId: '2021003',
|
|
|
+ projectTitle: '机器学习算法优化研究',
|
|
|
+ projectId: 3,
|
|
|
+ skills: ['Python', 'TensorFlow', '机器学习'],
|
|
|
+ experience: 5,
|
|
|
+ motivation: '对算法优化研究感兴趣,希望参与学术研究',
|
|
|
+ status: 'approved',
|
|
|
+ applyTime: '2024-01-10'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+// 匹配表单
|
|
|
+const matchingForm = reactive({
|
|
|
+ projectId: '',
|
|
|
+ algorithm: '',
|
|
|
+ teamSize: 3,
|
|
|
+ weights: {
|
|
|
+ skill: 40,
|
|
|
+ experience: 30,
|
|
|
+ learning: 20,
|
|
|
+ teamwork: 10
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 匹配结果
|
|
|
+const matchingResults = ref([
|
|
|
+ {
|
|
|
+ id: 1,
|
|
|
+ projectTitle: '全国大学生数学建模竞赛',
|
|
|
+ teamId: 'A',
|
|
|
+ matchScore: 92,
|
|
|
+ members: [
|
|
|
+ { id: 1, name: '张三', skills: ['数学建模', 'MATLAB', 'Python'], score: 4.5 },
|
|
|
+ { id: 2, name: '李四', skills: ['数据分析', 'R语言', '统计学'], score: 4.2 },
|
|
|
+ { id: 3, name: '王五', skills: ['算法设计', 'C++', '优化理论'], score: 4.8 }
|
|
|
+ ],
|
|
|
+ analysis: {
|
|
|
+ skillCoverage: 95,
|
|
|
+ experienceBalance: 88,
|
|
|
+ teamworkScore: 90
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 2,
|
|
|
+ projectTitle: '智能家居控制系统开发',
|
|
|
+ teamId: 'B',
|
|
|
+ matchScore: 87,
|
|
|
+ members: [
|
|
|
+ { id: 4, name: '赵六', skills: ['Java', 'Spring', '后端开发'], score: 4.3 },
|
|
|
+ { id: 5, name: '钱七', skills: ['React', 'Vue.js', '前端开发'], score: 4.1 },
|
|
|
+ { id: 6, name: '孙八', skills: ['物联网', '硬件开发', '嵌入式'], score: 4.6 },
|
|
|
+ { id: 7, name: '周九', skills: ['UI设计', '用户体验', 'Figma'], score: 4.0 }
|
|
|
+ ],
|
|
|
+ analysis: {
|
|
|
+ skillCoverage: 90,
|
|
|
+ experienceBalance: 85,
|
|
|
+ teamworkScore: 86
|
|
|
+ }
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
+// 筛选条件
|
|
|
+const filterProject = ref('')
|
|
|
+const filterStatus = ref('')
|
|
|
+
|
|
|
+// 对话框显示状态
|
|
|
+const showPublishDialog = ref(false)
|
|
|
+
|
|
|
+// 发布表单
|
|
|
+const publishForm = reactive({
|
|
|
+ title: '',
|
|
|
+ type: '',
|
|
|
+ requiredSkills: [],
|
|
|
+ teamSize: 3,
|
|
|
+ deadline: '',
|
|
|
+ reward: '',
|
|
|
+ description: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 可用技能列表
|
|
|
+const availableSkills = ref([
|
|
|
+ '数学建模', 'MATLAB', 'Python', '数据分析', 'Java', 'React', 'Vue.js',
|
|
|
+ '物联网', '机器学习', 'TensorFlow', '算法优化', 'C++', 'JavaScript',
|
|
|
+ 'UI设计', '用户体验', '硬件开发', '嵌入式', '后端开发', '前端开发'
|
|
|
+])
|
|
|
+
|
|
|
+// 过滤后的报名列表
|
|
|
+const filteredRegistrations = computed(() => {
|
|
|
+ let filtered = registrationList.value
|
|
|
+
|
|
|
+ if (filterProject.value) {
|
|
|
+ filtered = filtered.filter(reg => reg.projectId === filterProject.value)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (filterStatus.value) {
|
|
|
+ filtered = filtered.filter(reg => reg.status === filterStatus.value)
|
|
|
+ }
|
|
|
+
|
|
|
+ return filtered
|
|
|
+})
|
|
|
+
|
|
|
+// 处理标签页切换
|
|
|
+const handleTabChange = (tabName: string) => {
|
|
|
+ console.log('切换到标签页:', tabName)
|
|
|
+}
|
|
|
+
|
|
|
+// 获取项目类型样式
|
|
|
+const getProjectType = (type: string) => {
|
|
|
+ switch (type) {
|
|
|
+ case '学科竞赛': return 'danger'
|
|
|
+ case '创新项目': return 'primary'
|
|
|
+ case '实践项目': return 'success'
|
|
|
+ case '研究项目': return 'warning'
|
|
|
+ default: return 'info'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取状态类型
|
|
|
+const getStatusType = (status: string) => {
|
|
|
+ switch (status) {
|
|
|
+ case '报名中': return 'success'
|
|
|
+ case '进行中': return 'primary'
|
|
|
+ case '即将开始': return 'warning'
|
|
|
+ case '已结束': return 'info'
|
|
|
+ default: return 'info'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取报名状态类型
|
|
|
+const getRegistrationStatusType = (status: string) => {
|
|
|
+ switch (status) {
|
|
|
+ case 'approved': return 'success'
|
|
|
+ case 'pending': return 'warning'
|
|
|
+ case 'rejected': return 'danger'
|
|
|
+ default: return 'info'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取报名状态文本
|
|
|
+const getRegistrationStatusText = (status: string) => {
|
|
|
+ switch (status) {
|
|
|
+ case 'approved': return '已通过'
|
|
|
+ case 'pending': return '待审核'
|
|
|
+ case 'rejected': return '已拒绝'
|
|
|
+ default: return '未知'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 发布项目
|
|
|
+const publishProject = () => {
|
|
|
+ if (!publishForm.title || !publishForm.type || !publishForm.deadline) {
|
|
|
+ ElMessage.warning('请填写完整信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 这里应该调用API发布项目
|
|
|
+ showPublishDialog.value = false
|
|
|
+ Object.assign(publishForm, {
|
|
|
+ title: '', type: '', requiredSkills: [], teamSize: 3,
|
|
|
+ deadline: '', reward: '', description: ''
|
|
|
+ })
|
|
|
+ ElMessage.success('项目发布成功')
|
|
|
+}
|
|
|
+
|
|
|
+// 查看项目详情
|
|
|
+const viewProjectDetail = (project: any) => {
|
|
|
+ ElMessage.info('查看项目详情功能开发中...')
|
|
|
+}
|
|
|
+
|
|
|
+// 管理报名
|
|
|
+const manageApplicants = (project: any) => {
|
|
|
+ ElMessage.info('管理报名功能开发中...')
|
|
|
+}
|
|
|
+
|
|
|
+// 编辑项目
|
|
|
+const editProject = (project: any) => {
|
|
|
+ ElMessage.info('编辑项目功能开发中...')
|
|
|
+}
|
|
|
+
|
|
|
+// 查看学生档案
|
|
|
+const viewStudentProfile = (registration: any) => {
|
|
|
+ ElMessage.info('查看学生档案功能开发中...')
|
|
|
+}
|
|
|
+
|
|
|
+// 通过报名
|
|
|
+const approveRegistration = (registration: any) => {
|
|
|
+ registration.status = 'approved'
|
|
|
+ ElMessage.success('报名已通过')
|
|
|
+}
|
|
|
+
|
|
|
+// 拒绝报名
|
|
|
+const rejectRegistration = (registration: any) => {
|
|
|
+ registration.status = 'rejected'
|
|
|
+ ElMessage.success('报名已拒绝')
|
|
|
+}
|
|
|
+
|
|
|
+// 执行智能匹配
|
|
|
+const performMatching = () => {
|
|
|
+ if (!matchingForm.projectId || !matchingForm.algorithm) {
|
|
|
+ ElMessage.warning('请选择项目和匹配算法')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ElMessage.success('智能匹配执行成功')
|
|
|
+}
|
|
|
+
|
|
|
+// 查看详细分析
|
|
|
+const viewDetailedAnalysis = (result: any) => {
|
|
|
+ ElMessage.info('查看详细分析功能开发中...')
|
|
|
+}
|
|
|
+
|
|
|
+// 确认组队
|
|
|
+const confirmTeamFormation = (result: any) => {
|
|
|
+ ElMessage.success('团队组建确认成功')
|
|
|
+}
|
|
|
+
|
|
|
+// 调整团队
|
|
|
+const adjustTeam = (result: any) => {
|
|
|
+ ElMessage.info('调整团队功能开发中...')
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.competition-selection-container {
|
|
|
+ padding: 20px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ min-height: 100vh;
|
|
|
+
|
|
|
+ .function-nav {
|
|
|
+ background: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
|
+ margin-top: 20px;
|
|
|
+
|
|
|
+ .tab-content {
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .section-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ h3 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-filters {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .publish-section {
|
|
|
+ .project-list {
|
|
|
+ .project-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
+ transition: transform 0.3s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .project-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .project-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ h4 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .project-content {
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ .project-description {
|
|
|
+ color: #666;
|
|
|
+ line-height: 1.6;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .project-requirements {
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .label {
|
|
|
+ font-weight: 600;
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+
|
|
|
+ .skill-tag {
|
|
|
+ margin-right: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .project-details {
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+
|
|
|
+ .detail-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+
|
|
|
+ .el-icon {
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .project-stats {
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ padding: 12px;
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 8px;
|
|
|
+
|
|
|
+ .stat-item {
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ .stat-number {
|
|
|
+ display: block;
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .project-actions {
|
|
|
+ text-align: right;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ margin-left: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .registration-stats {
|
|
|
+ margin-bottom: 30px;
|
|
|
+
|
|
|
+ .stat-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
+
|
|
|
+ .stat-icon {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-right: 16px;
|
|
|
+
|
|
|
+ .el-icon {
|
|
|
+ color: white;
|
|
|
+ font-size: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-content {
|
|
|
+ .stat-number {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .registration-list {
|
|
|
+ .skill-tag {
|
|
|
+ margin-right: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .matching-config {
|
|
|
+ margin-bottom: 30px;
|
|
|
+ padding: 20px;
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 12px;
|
|
|
+
|
|
|
+ .matching-form {
|
|
|
+ .weight-item {
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ span {
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .matching-results {
|
|
|
+ .result-list {
|
|
|
+ .result-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
+
|
|
|
+ .result-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ h4 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-score {
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ .score-label {
|
|
|
+ display: block;
|
|
|
+ margin-top: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-content {
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ .team-members {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ h5 {
|
|
|
+ margin: 0 0 12px 0;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ }
|
|
|
+
|
|
|
+ .member-list {
|
|
|
+ .member-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 12px;
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .member-avatar {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-right: 12px;
|
|
|
+
|
|
|
+ .el-icon {
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .member-info {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .member-name {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .member-skills {
|
|
|
+ .el-tag {
|
|
|
+ margin-right: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .member-score {
|
|
|
+ margin-left: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .match-analysis {
|
|
|
+ h5 {
|
|
|
+ margin: 0 0 12px 0;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2c3e50;
|
|
|
+ }
|
|
|
+
|
|
|
+ .analysis-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .analysis-label {
|
|
|
+ width: 100px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-progress {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-actions {
|
|
|
+ text-align: right;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ margin-left: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|