培訓管理子系統開發3
整個項目預期的任務量 (任務量 = 所有工作的預期時間10天)和 目前已經花的時間 (所有記錄的 ‘已經花費的時間’3天),還剩余的時間(所有工作的 ‘剩余時間’7天)


實現了員工管理功能,查看員工選課情況

刪除功能如果員工存在選課記錄無法刪除

// 員工管理
@GetMapping("/employees")
public String employeeManage(Model model) {
// 獲取員工列表
List<User> employees = adminService.findAllEmployees();
// 獲取員工課程映射表
Map<Integer, List<Course>> coursesMap = adminService.getEmployeeCoursesMap();
model.addAttribute("employees", employees);
model.addAttribute("coursesMap", coursesMap);
return "admin/employee";
}
// 員工刪除
@PostMapping("/employees/delete/{id}")
public String deleteEmployee(@PathVariable Integer id,
HttpSession session,
RedirectAttributes ra) {
if (!checkAdmin(session)) return "redirect:/login";
try {
adminService.deleteEmployee(id);
ra.addFlashAttribute("success", "員工刪除成功");
} catch (AdminService.ServiceException e) {
ra.addFlashAttribute("error", e.getMessage());
} catch (Exception e) {
ra.addFlashAttribute("error", "系統錯誤:" + e.getMessage());
}
return "redirect:/admin/employees";
}
package com.example.training.repository;
import com.example.training.entity.EmployeeCourse;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface EmployeeCourseRepository extends JpaRepository<EmployeeCourse, Integer> {
@Query("SELECT ec FROM EmployeeCourse ec " +
"WHERE ec.employee.id = :employeeId " +
"AND ec.active = true")
List<EmployeeCourse> findActiveCoursesByEmployee(@Param("employeeId") Integer employeeId);
@Query("SELECT COUNT(ec) > 0 FROM EmployeeCourse ec " +
"WHERE ec.employee.id = :employeeId " +
"AND ec.course.id = :courseId " +
"AND ec.active = true")
boolean existsActiveSelection(@Param("employeeId") Integer employeeId,
@Param("courseId") Integer courseId);
@Query("UPDATE EmployeeCourse ec SET ec.active = false " +
"WHERE ec.employee.id = :employeeId " +
"AND ec.course.id = :courseId")
void cancelCourseSelection(@Param("employeeId") Integer employeeId,
@Param("courseId") Integer courseId);
@Query("SELECT COUNT(ec) > 0 FROM EmployeeCourse ec WHERE ec.course.id = :courseId")
boolean existsByCourseId(@Param("courseId") Integer courseId);
@Query("SELECT COUNT(ec) > 0 FROM EmployeeCourse ec WHERE ec.employee.id = :employeeId")
boolean existsByEmployeeId(@Param("employeeId") Integer employeeId);
@Query("SELECT ec FROM EmployeeCourse ec WHERE ec.employee.id = :employeeId AND ec.course.id = :courseId")
Optional<EmployeeCourse> findByEmployeeIdAndCourseId(
@Param("employeeId") Integer employeeId,
@Param("courseId") Integer courseId);
@Query("SELECT ec FROM EmployeeCourse ec JOIN FETCH ec.course WHERE ec.employee.id IN :employeeIds")
List<EmployeeCourse> findWithCoursesByEmployeeIds(@Param("employeeIds") List<Integer> employeeIds);
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>員工管理</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* 增強基礎樣式 */
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
margin: 0;
min-height: 100vh;
padding: 2rem;
}
.container {
background: rgba(255, 255, 255, 0.98);
border-radius: 16px;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
padding: 2.5rem;
max-width: 1000px;
margin: 0 auto;
backdrop-filter: blur(8px);
}
h2 {
color: #1e3c72;
margin: 0 0 2rem;
padding-bottom: 1rem;
border-bottom: 3px solid #1e3c72;
font-size: 2rem;
letter-spacing: -0.5px;
}
/* 提示信息樣式 */
.alert {
padding: 1rem 1.5rem;
border-radius: 8px;
margin: 1.5rem 0;
font-weight: 500;
}
.alert.success {
background: #e8f6ef;
color: #2e7d32;
border: 1px solid #a5d6a7;
}
.alert.error {
background: #ffebee;
color: #c62828;
border: 1px solid #ffcdd2;
}
/* 返回鏈接優化 */
.back-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: #2a5298;
padding: 0.8rem 1.2rem;
border-radius: 6px;
background: rgba(42,82,152,0.1);
transition: all 0.2s ease;
margin-bottom: 2rem;
}
.back-link:hover {
background: rgba(42,82,152,0.2);
transform: translateX(-3px);
text-decoration: none;
}
/* 表格增強 */
table {
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
background: white;
margin-top: 2rem;
}
th {
background: linear-gradient(135deg, #1e3c72, #2a5298);
color: white;
font-weight: 600;
padding: 1rem 1.5rem;
}
td {
padding: 1rem 1.5rem;
border-bottom: 1px solid #f0f4f8;
}
tr:hover {
background: #f8fafc !important;
}
/* 刪除按鈕樣式 */
.btn-delete {
background: #c62828 !important;
color: white !important;
padding: 0.5rem 1.2rem;
border-radius: 6px;
border: none;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
}
.btn-delete:hover {
background: #b71c1c !important;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* 響應式優化 */
@media (max-width: 768px) {
.container {
padding: 1.5rem;
}
table {
display: block;
overflow-x: auto;
}
th, td {
min-width: 120px;
}
}
.alert-container {
position: fixed;
top: 80px; /* 在導航欄下方 */
right: 20px;
z-index: 1000;
}
</style>
</head>
<body>
<div class="container">
<!-- 提示信息 -->
<div class="alert-container">
<div th:if="${success}" class="alert success" th:text="${success}"></div>
<div th:if="${error}" class="alert error" th:text="${error}"></div>
</div>
<h2>員工管理系統</h2>
<h3>員工列表</h3>
<table border="1">
<thead>
<tr>
<th>用戶名</th>
<th>已選課程</th>
<th style="width:120px;">操作</th>
</tr>
</thead>
<tbody>
<tr th:each="emp : ${employees}">
<td th:text="${emp.name}"></td>
<!-- 已選課程 -->
<td>
<div th:if="${coursesMap.containsKey(emp.id)}">
<div th:each="course : ${coursesMap.get(emp.id)}">
<span th:text="${course.name}"></span>
<small>(授課教師:<span th:text="${course.teacher}"></span>)</small>
</div>
</div>
<span th:unless="${coursesMap.containsKey(emp.id)}">未選擇任何課程</span>
</td>
<td>
<form th:action="@{/admin/employees/delete/{id}(id=${emp.id})}"
method="post"
onsubmit="return confirm('確定刪除該員工嗎?');">
<button type="submit" class="btn-delete">刪除</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>

浙公網安備 33010602011771號