Parcourir la source

admin-2025-04-17 02:19:06

genlitex il y a 1 semaine
Parent
commit
e89ac45645

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 4
dist/assets/index-BDQA6D8e.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/assets/index-DbBID-jA.css


Fichier diff supprimé car celui-ci est trop grand
+ 4 - 0
dist/assets/index-DdqLSOBX.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/assets/index-DezQ0Fgr.css


+ 2 - 2
dist/index.html

@@ -5,8 +5,8 @@
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Vite + Vue</title>
-    <script type="module" crossorigin src="/ide/proxy/6002/assets/index-BDQA6D8e.js"></script>
-    <link rel="stylesheet" crossorigin href="/ide/proxy/6002/assets/index-DezQ0Fgr.css">
+    <script type="module" crossorigin src="/ide/proxy/6003/assets/index-DdqLSOBX.js"></script>
+    <link rel="stylesheet" crossorigin href="/ide/proxy/6003/assets/index-DbBID-jA.css">
   </head>
   <body>
     <div id="app"></div>

+ 6 - 0
src/router/index.js

@@ -1,11 +1,17 @@
 import { createRouter, createWebHashHistory } from 'vue-router'
 import SalaryManagementView from '../views/SalaryManagementView.vue'
+import TrainingManagementView from '../views/TrainingManagementView.vue'
 
 const routes = [
   {
     path: '/',
     name: 'salary',
     component: SalaryManagementView
+  },
+  {
+    path: '/training',
+    name: 'training',
+    component: TrainingManagementView
   }
 ]
 

+ 737 - 0
src/views/TrainingManagementView.vue

@@ -0,0 +1,737 @@
+<template>
+  <div class="min-h-screen bg-gray-100 dark:bg-gray-900">
+    <!-- 顶部导航 -->
+    <div class="bg-white dark:bg-gray-800 shadow">
+      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
+        <div class="flex justify-between items-center">
+          <h1 class="text-2xl font-bold text-gray-900 dark:text-white">培训管理</h1>
+          <div class="flex space-x-4">
+            <button @click="showAddPlanModal = true" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 flex items-center">
+              <PlusCircle class="w-5 h-5 mr-1" />
+              创建培训计划
+            </button>
+            <button @click="showAddCourseModal = true" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 flex items-center">
+              <BookOpen class="w-5 h-5 mr-1" />
+              添加课程
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 主要内容区域 -->
+    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
+      <!-- 培训统计卡片 -->
+      <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
+        <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 flex items-center">
+          <div class="mr-4">
+            <Clock class="w-10 h-10 text-blue-600 dark:text-blue-400" />
+          </div>
+          <div>
+            <div class="text-sm font-medium text-gray-500 dark:text-gray-400">进行中培训</div>
+            <div class="mt-1 text-3xl font-bold text-blue-600 dark:text-blue-400">{{ ongoingTrainings }}</div>
+          </div>
+        </div>
+        <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 flex items-center">
+          <div class="mr-4">
+            <CheckCircle class="w-10 h-10 text-green-600 dark:text-green-400" />
+          </div>
+          <div>
+            <div class="text-sm font-medium text-gray-500 dark:text-gray-400">已完成培训</div>
+            <div class="mt-1 text-3xl font-bold text-green-600 dark:text-green-400">{{ completedTrainings }}</div>
+          </div>
+        </div>
+        <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 flex items-center">
+          <div class="mr-4">
+            <Users class="w-10 h-10 text-yellow-600 dark:text-yellow-400" />
+          </div>
+          <div>
+            <div class="text-sm font-medium text-gray-500 dark:text-gray-400">总参与人数</div>
+            <div class="mt-1 text-3xl font-bold text-yellow-600 dark:text-yellow-400">{{ totalParticipants }}</div>
+          </div>
+        </div>
+        <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 flex items-center">
+          <div class="mr-4">
+            <Star class="w-10 h-10 text-purple-600 dark:text-purple-400" />
+          </div>
+          <div>
+            <div class="text-sm font-medium text-gray-500 dark:text-gray-400">平均评分</div>
+            <div class="mt-1 text-3xl font-bold text-purple-600 dark:text-purple-400">{{ averageRating }}</div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 搜索和筛选 -->
+      <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
+        <div class="grid grid-cols-1 md:grid-cols-4 gap-4">
+          <div>
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">搜索</label>
+            <div class="relative">
+              <Search class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
+              <input 
+                type="text" 
+                v-model="searchText" 
+                placeholder="培训名称/课程名称" 
+                class="w-full pl-10 rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white"
+              >
+            </div>
+          </div>
+          <div>
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">培训类型</label>
+            <select v-model="filters.type" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+              <option value="">所有类型</option>
+              <option v-for="type in trainingTypes" :key="type.id" :value="type.id">{{ type.name }}</option>
+            </select>
+          </div>
+          <div>
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">状态</label>
+            <select v-model="filters.status" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+              <option value="">所有状态</option>
+              <option v-for="status in statusTypes" :key="status.id" :value="status.id">{{ status.name }}</option>
+            </select>
+          </div>
+          <div>
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">时间范围</label>
+            <select v-model="filters.timeRange" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+              <option value="">所有时间</option>
+              <option value="week">本周</option>
+              <option value="month">本月</option>
+              <option value="quarter">本季度</option>
+              <option value="year">本年</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <!-- 培训计划列表 -->
+      <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden mb-6">
+        <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
+          <h2 class="text-lg font-medium text-gray-900 dark:text-white flex items-center">
+            <ClipboardList class="w-5 h-5 mr-2" />
+            培训计划列表
+          </h2>
+        </div>
+        <div class="overflow-x-auto">
+          <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
+            <thead class="bg-gray-50 dark:bg-gray-700">
+              <tr>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">培训名称</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">类型</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">开始时间</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">结束时间</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">参与人数</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">状态</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">操作</th>
+              </tr>
+            </thead>
+            <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
+              <tr v-for="plan in filteredPlans" :key="plan.id">
+                <td class="px-6 py-4 whitespace-nowrap">
+                  <div class="text-sm font-medium text-gray-900 dark:text-white">{{ plan.name }}</div>
+                  <div class="text-sm text-gray-500 dark:text-gray-400">{{ plan.description }}</div>
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ getTrainingTypeName(plan.type) }}
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ formatDate(plan.startTime) }}
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ formatDate(plan.endTime) }}
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ plan.participants.length }}
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap">
+                  <span :class="getPlanStatusClass(plan.status)" class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full">
+                    {{ getPlanStatusName(plan.status) }}
+                  </span>
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
+                  <button @click="viewPlan(plan)" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300 mr-3 flex items-center">
+                    <Eye class="w-4 h-4 mr-1" />
+                    查看
+                  </button>
+                  <button @click="editPlan(plan)" class="text-yellow-600 hover:text-yellow-900 dark:text-yellow-400 dark:hover:text-yellow-300 mr-3 flex items-center">
+                    <Edit class="w-4 h-4 mr-1" />
+                    编辑
+                  </button>
+                  <button @click="deletePlan(plan)" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 flex items-center">
+                    <Trash2 class="w-4 h-4 mr-1" />
+                    删除
+                  </button>
+                </td>
+              </tr>
+              <tr v-if="filteredPlans.length === 0">
+                <td colspan="7" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
+                  没有找到匹配的培训计划
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+
+      <!-- 课程列表 -->
+      <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
+        <div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
+          <h2 class="text-lg font-medium text-gray-900 dark:text-white flex items-center">
+            <BookOpen class="w-5 h-5 mr-2" />
+            课程列表
+          </h2>
+        </div>
+        <div class="overflow-x-auto">
+          <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
+            <thead class="bg-gray-50 dark:bg-gray-700">
+              <tr>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">课程名称</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">类型</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">难度</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">时长</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">讲师</th>
+                <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">操作</th>
+              </tr>
+            </thead>
+            <tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
+              <tr v-for="course in filteredCourses" :key="course.id">
+                <td class="px-6 py-4 whitespace-nowrap">
+                  <div class="text-sm font-medium text-gray-900 dark:text-white">{{ course.name }}</div>
+                  <div class="text-sm text-gray-500 dark:text-gray-400">{{ course.description }}</div>
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ getTrainingTypeName(course.type) }}
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ getDifficultyLevelName(course.difficulty) }}
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ course.duration }}小时
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
+                  {{ course.instructor }}
+                </td>
+                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
+                  <button @click="viewCourse(course)" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300 mr-3 flex items-center">
+                    <Eye class="w-4 h-4 mr-1" />
+                    查看
+                  </button>
+                  <button @click="editCourse(course)" class="text-yellow-600 hover:text-yellow-900 dark:text-yellow-400 dark:hover:text-yellow-300 mr-3 flex items-center">
+                    <Edit class="w-4 h-4 mr-1" />
+                    编辑
+                  </button>
+                  <button @click="deleteCourse(course)" class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 flex items-center">
+                    <Trash2 class="w-4 h-4 mr-1" />
+                    删除
+                  </button>
+                </td>
+              </tr>
+              <tr v-if="filteredCourses.length === 0">
+                <td colspan="6" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
+                  没有找到匹配的课程
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+
+    <!-- 创建培训计划模态框 -->
+    <div v-if="showAddPlanModal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center z-50">
+      <div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full p-6">
+        <div class="flex justify-between items-center mb-4">
+          <h2 class="text-xl font-bold text-gray-900 dark:text-white flex items-center">
+            <ClipboardList class="w-5 h-5 mr-2" />
+            创建培训计划
+          </h2>
+          <button @click="showAddPlanModal = false" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
+            <X class="w-5 h-5" />
+          </button>
+        </div>
+        <form @submit.prevent="addPlan">
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">培训名称</label>
+            <input type="text" v-model="newPlan.name" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">培训类型</label>
+            <select v-model="newPlan.type" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+              <option v-for="type in trainingTypes" :key="type.id" :value="type.id">{{ type.name }}</option>
+            </select>
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">开始时间</label>
+            <input type="datetime-local" v-model="newPlan.startTime" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">结束时间</label>
+            <input type="datetime-local" v-model="newPlan.endTime" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">培训描述</label>
+            <textarea v-model="newPlan.description" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white"></textarea>
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">选择课程</label>
+            <div class="space-y-2 max-h-40 overflow-y-auto p-2 border border-gray-300 dark:border-gray-600 rounded-md">
+              <div v-for="course in courses" :key="course.id" class="flex items-center">
+                <input type="checkbox" :value="course.id" v-model="newPlan.courses" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600">
+                <span class="ml-2 text-sm text-gray-900 dark:text-white">{{ course.name }}</span>
+              </div>
+            </div>
+          </div>
+          <div class="flex justify-end space-x-3">
+            <button type="button" @click="showAddPlanModal = false" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center">
+              <X class="w-4 h-4 mr-1" />
+              取消
+            </button>
+            <button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center">
+              <Save class="w-4 h-4 mr-1" />
+              创建
+            </button>
+          </div>
+        </form>
+      </div>
+    </div>
+
+    <!-- 添加课程模态框 -->
+    <div v-if="showAddCourseModal" class="fixed inset-0 bg-gray-500 bg-opacity-75 flex items-center justify-center z-50">
+      <div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full p-6">
+        <div class="flex justify-between items-center mb-4">
+          <h2 class="text-xl font-bold text-gray-900 dark:text-white flex items-center">
+            <BookOpen class="w-5 h-5 mr-2" />
+            添加课程
+          </h2>
+          <button @click="showAddCourseModal = false" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300">
+            <X class="w-5 h-5" />
+          </button>
+        </div>
+        <form @submit.prevent="addCourse">
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">课程名称</label>
+            <input type="text" v-model="newCourse.name" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">课程类型</label>
+            <select v-model="newCourse.type" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+              <option v-for="type in trainingTypes" :key="type.id" :value="type.id">{{ type.name }}</option>
+            </select>
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">难度级别</label>
+            <select v-model="newCourse.difficulty" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+              <option v-for="level in difficultyLevels" :key="level.id" :value="level.id">{{ level.name }}</option>
+            </select>
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">课程时长(小时)</label>
+            <input type="number" v-model="newCourse.duration" required min="1" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">讲师</label>
+            <input type="text" v-model="newCourse.instructor" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
+          </div>
+          <div class="mb-4">
+            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">课程描述</label>
+            <textarea v-model="newCourse.description" required class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white"></textarea>
+          </div>
+          <div class="flex justify-end space-x-3">
+            <button type="button" @click="showAddCourseModal = false" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 flex items-center">
+              <X class="w-4 h-4 mr-1" />
+              取消
+            </button>
+            <button type="submit" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 flex items-center">
+              <Save class="w-4 h-4 mr-1" />
+              添加
+            </button>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue'
+import { useRouter } from 'vue-router'
+import { 
+  PlusCircle, 
+  BookOpen, 
+  Clock, 
+  CheckCircle, 
+  Users, 
+  Star, 
+  Search, 
+  ClipboardList, 
+  Eye, 
+  Edit, 
+  Trash2, 
+  X, 
+  Save 
+} from 'lucide-vue-next'
+
+// 路由
+const router = useRouter()
+
+// 培训计划数据
+const plans = ref([
+  {
+    id: 1,
+    name: '新员工入职培训',
+    type: 1,
+    startTime: '2024-03-01T09:00:00',
+    endTime: '2024-03-05T17:00:00',
+    description: '新员工入职培训计划,包含公司文化介绍和工作流程培训',
+    courses: [1, 2],
+    participants: [1, 2, 3, 4, 5],
+    status: 1,
+    rating: 4.5
+  },
+  {
+    id: 2,
+    name: '技术提升培训',
+    type: 2,
+    startTime: '2024-03-10T09:00:00',
+    endTime: '2024-03-12T17:00:00',
+    description: '技术提升培训计划,包含前端和后端开发进阶课程',
+    courses: [3, 4],
+    participants: [6, 7, 8, 9],
+    status: 2,
+    rating: 4.8
+  },
+  {
+    id: 3,
+    name: '管理技能培训',
+    type: 3,
+    startTime: '2024-04-01T09:00:00',
+    endTime: '2024-04-03T17:00:00',
+    description: '管理技能培训计划,针对新晋升的团队领导',
+    courses: [5],
+    participants: [10, 11, 12],
+    status: 1,
+    rating: 4.2
+  }
+])
+
+// 课程数据
+const courses = ref([
+  {
+    id: 1,
+    name: '公司文化介绍',
+    type: 1,
+    difficulty: 1,
+    duration: 2,
+    instructor: '张三',
+    description: '介绍公司文化和价值观,帮助新员工快速融入团队'
+  },
+  {
+    id: 2,
+    name: '工作流程培训',
+    type: 1,
+    difficulty: 1,
+    duration: 3,
+    instructor: '李四',
+    description: '介绍公司工作流程和规范,包括考勤、请假等制度'
+  },
+  {
+    id: 3,
+    name: '前端开发进阶',
+    type: 2,
+    difficulty: 3,
+    duration: 8,
+    instructor: '王五',
+    description: '前端开发高级技术培训,包括性能优化和组件设计'
+  },
+  {
+    id: 4,
+    name: '后端开发进阶',
+    type: 2,
+    difficulty: 3,
+    duration: 8,
+    instructor: '赵六',
+    description: '后端开发高级技术培训,包括系统架构和数据库优化'
+  },
+  {
+    id: 5,
+    name: '团队管理基础',
+    type: 3,
+    difficulty: 2,
+    duration: 6,
+    instructor: '钱七',
+    description: '团队管理基础培训,包括沟通技巧和冲突处理'
+  }
+])
+
+// 培训类型
+const trainingTypes = ref([
+  { id: 1, name: '入职培训' },
+  { id: 2, name: '技术培训' },
+  { id: 3, name: '管理培训' },
+  { id: 4, name: '产品培训' },
+  { id: 5, name: '销售培训' }
+])
+
+// 难度级别
+const difficultyLevels = ref([
+  { id: 1, name: '初级' },
+  { id: 2, name: '中级' },
+  { id: 3, name: '高级' }
+])
+
+// 状态类型
+const statusTypes = ref([
+  { id: 1, name: '进行中' },
+  { id: 2, name: '已完成' },
+  { id: 3, name: '已取消' }
+])
+
+// 筛选条件
+const searchText = ref('')
+const filters = ref({
+  type: '',
+  status: '',
+  timeRange: ''
+})
+
+// 模态框状态
+const showAddPlanModal = ref(false)
+const showAddCourseModal = ref(false)
+
+// 新培训计划表单
+const newPlan = ref({
+  name: '',
+  type: '',
+  startTime: '',
+  endTime: '',
+  description: '',
+  courses: []
+})
+
+// 新课程表单
+const newCourse = ref({
+  name: '',
+  type: '',
+  difficulty: '',
+  duration: '',
+  instructor: '',
+  description: ''
+})
+
+// 计算属性
+const filteredPlans = computed(() => {
+  return plans.value.filter(item => {
+    const matchesSearch = !searchText.value || 
+      item.name.toLowerCase().includes(searchText.value.toLowerCase()) ||
+      item.description.toLowerCase().includes(searchText.value.toLowerCase())
+    const matchesType = !filters.value.type || item.type === parseInt(filters.value.type)
+    const matchesStatus = !filters.value.status || item.status === parseInt(filters.value.status)
+    const matchesTimeRange = !filters.value.timeRange || checkTimeRange(item.startTime, filters.value.timeRange)
+    return matchesSearch && matchesType && matchesStatus && matchesTimeRange
+  })
+})
+
+const filteredCourses = computed(() => {
+  return courses.value.filter(item => {
+    const matchesSearch = !searchText.value || 
+      item.name.toLowerCase().includes(searchText.value.toLowerCase()) ||
+      item.description.toLowerCase().includes(searchText.value.toLowerCase())
+    const matchesType = !filters.value.type || item.type === parseInt(filters.value.type)
+    return matchesSearch && matchesType
+  })
+})
+
+const ongoingTrainings = computed(() => {
+  return plans.value.filter(item => item.status === 1).length
+})
+
+const completedTrainings = computed(() => {
+  return plans.value.filter(item => item.status === 2).length
+})
+
+const totalParticipants = computed(() => {
+  return plans.value.reduce((total, plan) => total + plan.participants.length, 0)
+})
+
+const averageRating = computed(() => {
+  const ratings = plans.value.map(plan => plan.rating || 0).filter(rating => rating > 0)
+  const sum = ratings.reduce((total, rating) => total + rating, 0)
+  return ratings.length > 0 ? (sum / ratings.length).toFixed(1) : '0.0'
+})
+
+// 格式化日期
+const formatDate = (date) => {
+  return new Date(date).toLocaleDateString('zh-CN', {
+    year: 'numeric',
+    month: '2-digit',
+    day: '2-digit',
+    hour: '2-digit',
+    minute: '2-digit'
+  })
+}
+
+// 检查时间范围
+const checkTimeRange = (date, range) => {
+  const now = new Date()
+  const targetDate = new Date(date)
+  
+  switch (range) {
+    case 'week': {
+      const weekStart = new Date(now)
+      weekStart.setDate(now.getDate() - now.getDay())
+      weekStart.setHours(0, 0, 0, 0)
+      const weekEnd = new Date(weekStart)
+      weekEnd.setDate(weekStart.getDate() + 7)
+      return targetDate >= weekStart && targetDate < weekEnd
+    }
+    case 'month': {
+      const monthStart = new Date(now.getFullYear(), now.getMonth(), 1)
+      const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0)
+      return targetDate >= monthStart && targetDate <= monthEnd
+    }
+    case 'quarter': {
+      const quarterMonth = Math.floor(now.getMonth() / 3) * 3
+      const quarterStart = new Date(now.getFullYear(), quarterMonth, 1)
+      const quarterEnd = new Date(now.getFullYear(), quarterMonth + 3, 0)
+      return targetDate >= quarterStart && targetDate <= quarterEnd
+    }
+    case 'year': {
+      const yearStart = new Date(now.getFullYear(), 0, 1)
+      const yearEnd = new Date(now.getFullYear(), 11, 31)
+      return targetDate >= yearStart && targetDate <= yearEnd
+    }
+    default:
+      return true
+  }
+}
+
+// 获取培训类型名称
+const getTrainingTypeName = (typeId) => {
+  const type = trainingTypes.value.find(t => t.id === typeId)
+  return type ? type.name : '未知类型'
+}
+
+// 获取难度级别名称
+const getDifficultyLevelName = (levelId) => {
+  const level = difficultyLevels.value.find(l => l.id === levelId)
+  return level ? level.name : '未知级别'
+}
+
+// 获取计划状态名称
+const getPlanStatusName = (statusId) => {
+  const status = statusTypes.value.find(s => s.id === statusId)
+  return status ? status.name : '未知状态'
+}
+
+// 获取计划状态样式
+const getPlanStatusClass = (statusId) => {
+  const classes = {
+    1: 'bg-blue-100 text-blue-800 dark:bg-blue-700 dark:text-blue-300',
+    2: 'bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-300',
+    3: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'
+  }
+  return classes[statusId] || 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'
+}
+
+// 添加培训计划
+const addPlan = () => {
+  const item = {
+    id: plans.value.length + 1,
+    ...newPlan.value,
+    participants: [],
+    status: 1,
+    rating: 0
+  }
+  plans.value.push(item)
+  showAddPlanModal.value = false
+  resetNewPlan()
+}
+
+// 重置新培训计划表单
+const resetNewPlan = () => {
+  newPlan.value = {
+    name: '',
+    type: '',
+    startTime: '',
+    endTime: '',
+    description: '',
+    courses: []
+  }
+}
+
+// 添加课程
+const addCourse = () => {
+  const item = {
+    id: courses.value.length + 1,
+    ...newCourse.value
+  }
+  courses.value.push(item)
+  showAddCourseModal.value = false
+  resetNewCourse()
+}
+
+// 重置新课程表单
+const resetNewCourse = () => {
+  newCourse.value = {
+    name: '',
+    type: '',
+    difficulty: '',
+    duration: '',
+    instructor: '',
+    description: ''
+  }
+}
+
+// 查看培训计划
+const viewPlan = (item) => {
+  // 实现查看培训计划逻辑
+  console.log('查看培训计划:', item)
+  // 这里可以跳转到培训详情页面
+  // router.push(`/training/${item.id}`)
+}
+
+// 编辑培训计划
+const editPlan = (item) => {
+  // 实现编辑培训计划逻辑
+  console.log('编辑培训计划:', item)
+  // 这里可以打开编辑模态框,预填充当前数据
+  newPlan.value = { ...item }
+  showAddPlanModal.value = true
+}
+
+// 删除培训计划
+const deletePlan = (item) => {
+  if (confirm(`确定要删除培训计划"${item.name}"吗?`)) {
+    const index = plans.value.findIndex(p => p.id === item.id)
+    if (index !== -1) {
+      plans.value.splice(index, 1)
+    }
+  }
+}
+
+// 查看课程
+const viewCourse = (item) => {
+  // 实现查看课程逻辑
+  console.log('查看课程:', item)
+  // 这里可以跳转到课程详情页面
+  // router.push(`/course/${item.id}`)
+}
+
+// 编辑课程
+const editCourse = (item) => {
+  // 实现编辑课程逻辑
+  console.log('编辑课程:', item)
+  // 这里可以打开编辑模态框,预填充当前数据
+  newCourse.value = { ...item }
+  showAddCourseModal.value = true
+}
+
+// 删除课程
+const deleteCourse = (item) => {
+  if (confirm(`确定要删除课程"${item.name}"吗?`)) {
+    const index = courses.value.findIndex(c => c.id === item.id)
+    if (index !== -1) {
+      courses.value.splice(index, 1)
+    }
+  }
+}
+</script>

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff