feat: 实现成长路径功能
All checks were successful
continuous-integration/drone/push Build is passing

- 新增数据库表: growth_path_nodes, user_growth_path_progress, user_node_completions
- 新增 Model: GrowthPathNode, UserGrowthPathProgress, UserNodeCompletion
- 新增 Service: GrowthPathService(管理端CRUD、学员端进度追踪)
- 新增 API: 学员端获取成长路径、管理端CRUD
- 前端学员端从API动态加载成长路径数据
- 更新管理端API接口定义
This commit is contained in:
yuliang_guo
2026-01-30 15:37:14 +08:00
parent d44111e712
commit b4906c543b
11 changed files with 1816 additions and 154 deletions

View File

@@ -363,7 +363,7 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
@@ -374,7 +374,8 @@ import {
Cpu // 机器人图标
} from '@element-plus/icons-vue'
import * as echarts from 'echarts'
import { analyzeYanjiBadge, getCourseDetail } from '@/api/trainee'
import { analyzeYanjiBadge, getCourseDetail, getGrowthPath, startGrowthPath } from '@/api/trainee'
import type { TraineeGrowthPath, TraineeGrowthPathStage } from '@/api/trainee'
import { getCurrentUserProfile } from '@/api/user'
const router = useRouter()
@@ -451,100 +452,73 @@ const expPercentage = computed(() => {
return Math.round((userInfo.value.exp / userInfo.value.nextLevelExp) * 100)
})
// 成长路径数据
const growthPath = ref([
{
name: '基础阶段',
completed: 3,
total: 3,
nodes: [
{
id: 1,
title: '机构文化与服务理念',
description: '了解机构的服务理念和专业文化',
status: 'completed',
progress: 100
},
{
id: 2,
title: '美容产品基础知识',
description: '学习美容产品的基本知识和使用方法',
status: 'completed',
progress: 100
},
{
id: 3,
title: '客户接待与服务标准',
description: '掌握专业的客户接待和服务流程',
status: 'completed',
progress: 100
}
]
},
{
name: '进阶阶段',
completed: 2,
total: 4,
nodes: [
{
id: 4,
title: '皮肤分析技术',
description: '学习专业的皮肤分析方法和技术',
status: 'completed',
progress: 100
},
{
id: 5,
title: '美容仪器操作',
description: '掌握各种美容仪器的操作方法',
status: 'completed',
progress: 100
},
{
id: 6,
title: '客户沟通进阶',
description: '学习高级的客户沟通和咨询技巧',
status: 'current',
progress: 65
},
{
id: 7,
title: '美容方案设计',
description: '学习为客户设计个性化的美容方案',
status: 'locked',
progress: 0
}
]
},
{
name: '高级阶段',
completed: 0,
total: 3,
nodes: [
{
id: 8,
title: '轻医美项目咨询',
description: '掌握轻医美项目的专业咨询技能',
status: 'locked',
progress: 0
},
{
id: 9,
title: '团队协作与管理',
description: '学习团队协作和基础管理技能',
status: 'locked',
progress: 0
},
{
id: 10,
title: '高级美容技术',
description: '掌握高级美容技术和创新方法',
status: 'locked',
progress: 0
}
]
// 成长路径数据从API加载
const growthPathData = ref<TraineeGrowthPath | null>(null)
const growthPathLoading = ref(false)
// 将API数据转换为前端展示格式
const growthPath = computed(() => {
if (!growthPathData.value || !growthPathData.value.stages) {
// 返回默认空数据或占位数据
return [{
name: '暂无成长路径',
completed: 0,
total: 0,
nodes: []
}]
}
])
return growthPathData.value.stages.map(stage => ({
name: stage.name,
completed: stage.completed,
total: stage.total,
nodes: stage.nodes.map(node => ({
id: node.id,
courseId: node.course_id,
title: node.title,
description: node.description || '',
status: node.status, // locked/unlocked/in_progress/completed
progress: node.progress
}))
}))
})
/**
* 加载成长路径数据
*/
const loadGrowthPath = async () => {
growthPathLoading.value = true
try {
const response = await getGrowthPath()
if (response.code === 200 && response.data) {
growthPathData.value = response.data
}
} catch (error) {
console.error('加载成长路径失败:', error)
// 失败时不显示错误,保持空状态
} finally {
growthPathLoading.value = false
}
}
/**
* 开始学习成长路径
*/
const handleStartGrowthPath = async () => {
if (!growthPathData.value) return
try {
const response = await startGrowthPath(growthPathData.value.id)
if (response.code === 200) {
ElMessage.success('已开始学习成长路径')
// 重新加载数据
await loadGrowthPath()
}
} catch (error: any) {
console.error('开始学习失败:', error)
ElMessage.error(error.response?.data?.detail || '开始学习失败')
}
}
/**
* 初始化雷达图
@@ -754,12 +728,12 @@ const handleNodeClick = (node: any) => {
return
}
if (node.status === 'completed') {
ElMessage.info(`${node.title} 已完成`)
return
// 跳转到课程详情页
if (node.courseId) {
router.push(`/trainee/course-detail?id=${node.courseId}`)
} else {
ElMessage.info(`${node.title}`)
}
ElMessage.success(`开始学习:${node.title}`)
}
/**
@@ -921,6 +895,9 @@ onMounted(async () => {
// 获取用户信息
await fetchUserInfo()
// 加载成长路径数据
await loadGrowthPath()
nextTick(() => {
initRadarChart()
})