diff --git a/backend/database/migrate.go b/backend/database/migrate.go index a793872..97b23d6 100644 --- a/backend/database/migrate.go +++ b/backend/database/migrate.go @@ -2,6 +2,7 @@ package database import ( "log" + "progress-wall-backend/models" "gorm.io/gorm" @@ -13,8 +14,10 @@ func Migrate(db *gorm.DB) error { // 迁移所有模型 err := db.AutoMigrate( - // 用户和权限相关 + // 用户相关 &models.User{}, + + // 团队相关 &models.Team{}, &models.TeamMember{}, @@ -34,7 +37,6 @@ func Migrate(db *gorm.DB) error { // 活动日志 &models.ActivityLog{}, ) - if err != nil { log.Printf("数据库迁移失败: %v", err) return err diff --git a/backend/handlers/project/project.go b/backend/handlers/project/project.go index ed7c44c..b41c76b 100644 --- a/backend/handlers/project/project.go +++ b/backend/handlers/project/project.go @@ -25,6 +25,7 @@ func NewProjectHandler(db *gorm.DB) *ProjectHandler { } // GetProject 获取单个项目 +// GET /api/projects/:projectId func (h *ProjectHandler) GetProject(c *gin.Context) { projectID, err := strconv.ParseUint(c.Param("projectId"), 10, 32) if err != nil { @@ -45,7 +46,8 @@ func (h *ProjectHandler) GetProject(c *gin.Context) { c.JSON(http.StatusOK, project) } -// GetProjects 获取用户的所有项目 +// GetProjects 获取用户的所有项目 (Deprecated or kept for "All Projects" view) +// GET /api/projects func (h *ProjectHandler) GetProjects(c *gin.Context) { userID := c.GetUint("user_id") if userID == 0 { @@ -62,34 +64,43 @@ func (h *ProjectHandler) GetProjects(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"projects": projects}) } +// CreateProject 创建项目 // POST /api/teams/:teamId/projects func (h *ProjectHandler) CreateProject(c *gin.Context) { - userID := c.GetUint("user_id") // Set by AuthMiddleware + userID := c.GetUint("user_id") if userID == 0 { c.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户信息"}) return } teamIDStr := c.Param("teamId") - teamID, err := strconv.ParseUint(teamIDStr, 10, 32) // Should already validated by RBAC Middleware + teamID, err := strconv.ParseUint(teamIDStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Team ID"}) return } var createProjectRequest struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` + Name string `json:"name" binding:"required,min=1,max=100"` + Description string `json:"description" binding:"max=500"` Status *int `json:"status"` StartDate *time.Time `json:"start_date"` EndDate *time.Time `json:"end_date"` } if err := c.ShouldBindJSON(&createProjectRequest); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"}) + c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误: " + err.Error()}) return } + // Validate dates + if createProjectRequest.StartDate != nil && createProjectRequest.EndDate != nil { + if createProjectRequest.EndDate.Before(*createProjectRequest.StartDate) { + c.JSON(http.StatusBadRequest, gin.H{"error": "结束时间不能早于开始时间"}) + return + } + } + status := models.ProjectStatusActive if createProjectRequest.Status != nil { status = models.ProjectStatus(*createProjectRequest.Status) @@ -113,11 +124,15 @@ func (h *ProjectHandler) CreateProject(c *gin.Context) { c.JSON(http.StatusCreated, project) } -// Gets all projects for a specific team. +// GetTeamProjects Gets all projects for a specific team. // GET /api/teams/:teamId/projects func (h *ProjectHandler) GetTeamProjects(c *gin.Context) { teamIDStr := c.Param("teamId") - teamID, _ := strconv.ParseUint(teamIDStr, 10, 32) // Already validated by RBAC Middleware + teamID, err := strconv.ParseUint(teamIDStr, 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Team ID"}) + return + } projects, err := h.projectService.GetTeamProjects(uint(teamID)) if err != nil { @@ -129,6 +144,7 @@ func (h *ProjectHandler) GetTeamProjects(c *gin.Context) { } // UpdateProject 更新项目 +// PUT /api/projects/:projectId func (h *ProjectHandler) UpdateProject(c *gin.Context) { projectID, err := strconv.ParseUint(c.Param("projectId"), 10, 32) if err != nil { @@ -137,15 +153,15 @@ func (h *ProjectHandler) UpdateProject(c *gin.Context) { } var updateProjectRequest struct { - Name *string `json:"name"` - Description *string `json:"description"` + Name *string `json:"name" binding:"omitempty,min=1,max=100"` + Description *string `json:"description" binding:"omitempty,max=500"` Status *models.ProjectStatus `json:"status"` StartDate *time.Time `json:"start_date"` EndDate *time.Time `json:"end_date"` } if err := c.ShouldBindJSON(&updateProjectRequest); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"}) + c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误: " + err.Error()}) return } @@ -179,6 +195,7 @@ func (h *ProjectHandler) UpdateProject(c *gin.Context) { } // DeleteProject 删除项目 +// DELETE /api/projects/:projectId func (h *ProjectHandler) DeleteProject(c *gin.Context) { projectID, err := strconv.ParseUint(c.Param("projectId"), 10, 32) if err != nil { diff --git a/backend/handlers/team/team.go b/backend/handlers/team/team.go index 263a5ab..661f71e 100644 --- a/backend/handlers/team/team.go +++ b/backend/handlers/team/team.go @@ -21,20 +21,20 @@ func NewTeamHandler(db *gorm.DB) *TeamHandler { } } -// POST /api/teams -func (h *TeamHandler) CreateTeam(c *gin.Context) { - userID := c.GetUint("user_id") // Get from AuthMiddleware - - var req struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - } +type CreateTeamRequest struct { + Name string `json:"name" binding:"required"` + Description string `json:"description"` +} +// CreateTeam 创建团队 +func (h *TeamHandler) CreateTeam(c *gin.Context) { + var req CreateTeamRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameters"}) + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + userID := c.GetUint("user_id") team, err := h.teamService.CreateTeam(req.Name, req.Description, userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) @@ -44,67 +44,88 @@ func (h *TeamHandler) CreateTeam(c *gin.Context) { c.JSON(http.StatusCreated, team) } -// Gets all teams a user belongs to. -// GET /api/teams +// GetMyTeams 获取当前用户的团队列表 func (h *TeamHandler) GetMyTeams(c *gin.Context) { userID := c.GetUint("user_id") - teams, err := h.teamService.GetUserTeams(userID) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch teams: " + err.Error()}) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"teams": teams}) } -// POST /api/teams/:teamId/members -func (h *TeamHandler) AddMember(c *gin.Context) { +// GetTeam 获取单个团队详情 +func (h *TeamHandler) GetTeam(c *gin.Context) { teamIDStr := c.Param("teamId") teamID, err := strconv.ParseUint(teamIDStr, 10, 32) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Team ID"}) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid team ID"}) return } - // Who to add, and what role - var req struct { - UserID uint `json:"user_id" binding:"required"` - Role models.TeamRole `json:"role"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameters"}) + team, err := h.teamService.GetTeamByID(uint(teamID)) + if err != nil { + if err == services.ErrTeamNotFound { + c.JSON(http.StatusNotFound, gin.H{"error": "Team not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - // Default to Member if role is invalid - if req.Role != models.TeamRoleAdmin { - req.Role = models.TeamRoleMember + c.JSON(http.StatusOK, team) +} + +// GetTeamMembers 获取团队成员 +func (h *TeamHandler) GetTeamMembers(c *gin.Context) { + teamIDStr := c.Param("teamId") + teamID, err := strconv.ParseUint(teamIDStr, 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid team ID"}) + return } - if err := h.teamService.AddMember(uint(teamID), req.UserID, req.Role); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + members, err := h.teamService.GetMembers(uint(teamID)) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - c.JSON(http.StatusOK, gin.H{"message": "Member added successfully"}) + c.JSON(http.StatusOK, gin.H{"members": members}) } -// GET /api/teams/:teamId/members -func (h *TeamHandler) GetTeamMembers(c *gin.Context) { +// AddMember 添加成员 +func (h *TeamHandler) AddMember(c *gin.Context) { teamIDStr := c.Param("teamId") teamID, err := strconv.ParseUint(teamIDStr, 10, 32) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Team ID"}) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid team ID"}) return } - members, err := h.teamService.GetTeamMembers(uint(teamID)) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch team members: " + err.Error()}) + var req struct { + UserID uint `json:"user_id" binding:"required"` + Role models.TeamRole `json:"role"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - c.JSON(http.StatusOK, gin.H{"members": members}) + if req.Role == 0 { + req.Role = models.TeamRoleMember // Default to member + } + + if err := h.teamService.AddTeamMember(uint(teamID), req.UserID, req.Role); err != nil { + if err == services.ErrUserAlreadyMember { + c.JSON(http.StatusConflict, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Member added successfully"}) } diff --git a/backend/routes/routes.go b/backend/routes/routes.go index a169f44..7f863ec 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -72,6 +72,10 @@ func SetupRoutes(db *gorm.DB, cfg *config.Config) *gin.Engine { // Team Routes protected.POST("/teams", teamHandler.CreateTeam) protected.GET("/teams", teamHandler.GetMyTeams) + protected.GET("/teams/:teamId", + rbac.RequireTeamAccess("view", "teamId"), + teamHandler.GetTeam, + ) protected.GET("/teams/:teamId/members", rbac.RequireTeamAccess("view", "teamId"), teamHandler.GetTeamMembers, @@ -91,9 +95,18 @@ func SetupRoutes(db *gorm.DB, cfg *config.Config) *gin.Engine { projectHandler.GetTeamProjects, ) protected.GET("/projects", projectHandler.GetProjects) - protected.GET("/projects/:projectId", projectHandler.GetProject) - protected.PUT("/projects/:projectId", projectHandler.UpdateProject) - protected.DELETE("/projects/:projectId", projectHandler.DeleteProject) + protected.GET("/projects/:projectId", + rbac.RequireProjectAccess("view", "projectId", "project"), + projectHandler.GetProject, + ) + protected.PUT("/projects/:projectId", + rbac.RequireProjectAccess("manage", "projectId", "project"), + projectHandler.UpdateProject, + ) + protected.DELETE("/projects/:projectId", + rbac.RequireProjectAccess("manage", "projectId", "project"), + projectHandler.DeleteProject, + ) // 看板相关 protected.GET("/boards", boardHandler.GetBoards) diff --git a/backend/services/project_service.go b/backend/services/project_service.go index 262d035..3ccdf38 100644 --- a/backend/services/project_service.go +++ b/backend/services/project_service.go @@ -9,19 +9,19 @@ import ( "gorm.io/gorm" ) -// ProjectService 项目服务 +// ProjectService provides methods for managing projects. type ProjectService struct { db *gorm.DB } -// NewProjectService 创建项目服务 +// NewProjectService creates a new instance of ProjectService. func NewProjectService(db *gorm.DB) *ProjectService { return &ProjectService{ db: db, } } -// GetProjectByID 根据ID获取项目 +// GetProjectByID retrieves a project by its ID, including its owner, boards, and members. func (s *ProjectService) GetProjectByID(projectID uint) (*models.Project, error) { var project models.Project result := s.db. @@ -40,7 +40,7 @@ func (s *ProjectService) GetProjectByID(projectID uint) (*models.Project, error) return &project, nil } -// GetProjectsByUserID 获取用户的所有项目 +// GetProjectsByUserID retrieves all projects owned by a specific user. func (s *ProjectService) GetProjectsByUserID(userID uint) ([]models.Project, error) { var projects []models.Project result := s.db. @@ -56,7 +56,8 @@ func (s *ProjectService) GetProjectsByUserID(userID uint) ([]models.Project, err return projects, nil } -// Creates a new project under a team and assigns the creator as ProjectAdmin. +// CreateProject creates a new project under a team and assigns the creator as ProjectAdmin. +// It executes the creation and member assignment within a transaction. func (s *ProjectService) CreateProject(project *models.Project) error { tx := s.db.Begin() defer func() { @@ -94,14 +95,14 @@ func (s *ProjectService) CreateProject(project *models.Project) error { return nil } -// Gets all projects for a specific team. +// GetTeamProjects retrieves all projects associated with a specific team. func (s *ProjectService) GetTeamProjects(teamID uint) ([]models.Project, error) { var projects []models.Project err := s.db.Where("team_id = ?", teamID).Find(&projects).Error return projects, err } -// UpdateProject 更新项目 +// UpdateProject updates the fields of a project identified by projectID. func (s *ProjectService) UpdateProject(projectID uint, updates map[string]interface{}) error { result := s.db.Model(&models.Project{}).Where("id = ?", projectID).Updates(updates) if result.Error != nil { @@ -113,7 +114,7 @@ func (s *ProjectService) UpdateProject(projectID uint, updates map[string]interf return nil } -// DeleteProject 删除项目(软删除) +// DeleteProject soft-deletes a project by its ID. func (s *ProjectService) DeleteProject(projectID uint) error { result := s.db.Delete(&models.Project{}, projectID) if result.Error != nil { diff --git a/backend/services/team_service.go b/backend/services/team_service.go index b9d258a..6ab1f3a 100644 --- a/backend/services/team_service.go +++ b/backend/services/team_service.go @@ -2,11 +2,18 @@ package services import ( "errors" + "fmt" + "progress-wall-backend/models" "gorm.io/gorm" ) +var ( + ErrTeamNotFound = errors.New("team not found") + ErrUserAlreadyMember = errors.New("user is already a member of the team") +) + type TeamService struct { db *gorm.DB } @@ -15,89 +22,87 @@ func NewTeamService(db *gorm.DB) *TeamService { return &TeamService{db: db} } -// Creates a new team and assigns the creator as TeamAdmin. +// CreateTeam 创建团队并自动将创建者设为管理员 func (s *TeamService) CreateTeam(name, description string, creatorID uint) (*models.Team, error) { - tx := s.db.Begin() - defer func() { - if r := recover(); r != nil { - tx.Rollback() - } - }() - - // Create the Team team := &models.Team{ Name: name, Description: description, CreatorID: creatorID, } - if err := tx.Create(team).Error; err != nil { - tx.Rollback() - return nil, err - } - // Add Creator as TeamAdmin - member := &models.TeamMember{ - TeamID: team.ID, - UserID: creatorID, - Role: models.TeamRoleAdmin, - } - if err := tx.Create(member).Error; err != nil { - tx.Rollback() - return nil, err - } + err := s.db.Transaction(func(tx *gorm.DB) error { + if err := tx.Create(team).Error; err != nil { + return err + } - if err := tx.Commit().Error; err != nil { - return nil, err + // Add creator as admin + member := &models.TeamMember{ + TeamID: team.ID, + UserID: creatorID, + Role: models.TeamRoleAdmin, + } + if err := tx.Create(member).Error; err != nil { + return err + } + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to create team: %v", err) } return team, nil } -// Adds a user to the team. -func (s *TeamService) AddMember(teamID, userID uint, role models.TeamRole) error { - // Check if user exists - var count int64 - s.db.Model(&models.User{}).Where("id = ?", userID).Count(&count) - if count == 0 { - return errors.New("user not found") - } - - // Check if already a member - var member models.TeamMember - err := s.db.Where("team_id = ? AND user_id = ?", teamID, userID).First(&member).Error - if err == nil { - return errors.New("user is already a member of this team") - } - - // Add member - newMember := models.TeamMember{ - TeamID: teamID, - UserID: userID, - Role: role, - } - return s.db.Create(&newMember).Error -} - -// Gets all teams a user belongs to. +// GetUserTeams 获取用户参与的所有团队 func (s *TeamService) GetUserTeams(userID uint) ([]models.Team, error) { var teams []models.Team - // Select teams where user_id in team_members matches - err := s.db.Joins("JOIN team_members ON team_members.team_id = teams.id"). + // 通过 team_members 关联查询 + err := s.db.Model(&models.Team{}). + Joins("JOIN team_members ON team_members.team_id = teams.id"). Where("team_members.user_id = ?", userID). + Preload("Creator"). Find(&teams).Error - if err != nil { - return nil, err + return nil, fmt.Errorf("failed to fetch user teams: %v", err) } - return teams, err + + return teams, nil } -// Gets team members for a specific team -func (s *TeamService) GetTeamMembers(teamID uint) ([]models.TeamMember, error) { - var members []models.TeamMember - err := s.db.Where("team_id = ?", teamID).Find(&members).Error +// GetTeamByID 获取团队详情 +func (s *TeamService) GetTeamByID(teamID uint) (*models.Team, error) { + var team models.Team + err := s.db.Preload("Creator").Preload("Members").First(&team, teamID).Error if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrTeamNotFound + } return nil, err } + return &team, nil +} + +// GetMembers 获取团队成员列表 +func (s *TeamService) GetMembers(teamID uint) ([]models.TeamMember, error) { + var members []models.TeamMember + err := s.db.Where("team_id = ?", teamID).Preload("User").Find(&members).Error return members, err } + +// AddTeamMember 添加成员到团队 +func (s *TeamService) AddTeamMember(teamID, userID uint, role models.TeamRole) error { + // Check if already exists + var count int64 + s.db.Model(&models.TeamMember{}).Where("team_id = ? AND user_id = ?", teamID, userID).Count(&count) + if count > 0 { + return ErrUserAlreadyMember + } + + member := models.TeamMember{ + TeamID: teamID, + UserID: userID, + Role: role, + } + + return s.db.Create(&member).Error +} diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 9719488..6f71b92 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -7,7 +7,13 @@ const routes: RouteRecordRaw[] = [ { path: '/', name: 'Home', - component: () => import('@/views/HomeView.vue') + redirect: (to) => { + const userStore = useUserStore() + if (userStore.isLoggedIn) { + return '/teams' + } + return '/login' + } }, { path: '/login', @@ -19,6 +25,18 @@ const routes: RouteRecordRaw[] = [ name: 'Register', component: () => import('@/views/user/RegisterView.vue') }, + { + path: '/teams', + name: 'TeamList', + component: () => import('@/views/team/TeamListView.vue'), + meta: { requiresAuth: true } + }, + { + path: '/teams/:teamId(\\d+)/projects', + name: 'ProjectList', + component: () => import('@/views/project/ProjectListView.vue'), + meta: { requiresAuth: true } + }, { path: '/projects/:projectId(\\d+)/boards', name: 'ProjectBoards', @@ -26,7 +44,7 @@ const routes: RouteRecordRaw[] = [ meta: { requiresAuth: true } }, { - path: '/kanban', + path: '/kanban/:boardId(\\d+)', name: 'Kanban', component: () => import('@/views/dashboard/KanbanView.vue'), meta: { requiresAuth: true } @@ -71,7 +89,7 @@ router.beforeEach((to, from, next) => { }) } else if (userStore.isLoggedIn && (to.path === '/login' || to.path === '/register')) { // 已登录用户访问登录/注册页,重定向到首页 - next('/') + next('/teams') } else { next() } diff --git a/frontend/src/services/board-api.ts b/frontend/src/services/board-api.ts index 13666c9..50803ad 100644 --- a/frontend/src/services/board-api.ts +++ b/frontend/src/services/board-api.ts @@ -68,8 +68,10 @@ export class BoardApiService { // 获取看板列表 async getKanbanList(): Promise> { try { - const response = await api.get('/boards') - return response.data + const response = await api.get<{ boards: Board[] }>('/boards') + return { + data: response.data.boards + } } catch (error: any) { return { msg: error.response?.data?.msg || error.message || '获取看板列表失败', @@ -78,6 +80,21 @@ export class BoardApiService { } } + // Get boards by project + async getKanbanListByProject(projectId: string): Promise> { + try { + const response = await api.get<{ boards: Board[] }>(`/projects/${projectId}/boards`) + return { + data: response.data.boards + } + } catch (error: any) { + return { + msg: error.response?.data?.msg || error.message || 'Failed to fetch boards', + data: [] + } + } + } + /** * 获取单个看板详情(包含嵌套的列和任务) * 需要认证: Bearer Token (JWT) diff --git a/frontend/src/services/index.ts b/frontend/src/services/index.ts index d0285cd..d2c6d1c 100644 --- a/frontend/src/services/index.ts +++ b/frontend/src/services/index.ts @@ -16,4 +16,5 @@ export { type TaskDetailResponse, type MoveTaskRequest, type MoveTaskResponse -} from './task-api' \ No newline at end of file +} from './task-api' +export * from './team-api' diff --git a/frontend/src/services/team-api.ts b/frontend/src/services/team-api.ts new file mode 100644 index 0000000..35f1f8f --- /dev/null +++ b/frontend/src/services/team-api.ts @@ -0,0 +1,57 @@ +import api from '@/lib/api' + +export interface Team { + id: number + name: string + description: string + creator_id: number + created_at: string + updated_at: string +} + +export interface TeamMember { + id: number + team_id: number + user_id: number + role: number // 1: Member, 2: Admin + joined_at: string + user?: { + id: number + username: string + nickname: string + avatar: string + } +} + +export interface CreateTeamDto { + name: string + description?: string +} + +export const teamApi = { + // Get all teams for the current user + getMyTeams: () => { + return api.get<{ teams: Team[] }>('/teams') + }, + + // Create a new team + createTeam: (data: CreateTeamDto) => { + return api.post('/teams', data) + }, + + // Get team details + getTeam: (teamId: number) => { + return api.get(`/teams/${teamId}`) + }, + + // Get team members + getMembers: (teamId: number) => { + return api.get<{ members: TeamMember[] }>(`/teams/${teamId}/members`) + }, + + // Add member to team + addMember: (teamId: number, userId: number, role: number = 1) => { + return api.post(`/teams/${teamId}/members`, { user_id: userId, role }) + } +} + diff --git a/frontend/src/stores/board.ts b/frontend/src/stores/board.ts index 9ff6fba..f2190a0 100644 --- a/frontend/src/stores/board.ts +++ b/frontend/src/stores/board.ts @@ -24,8 +24,8 @@ export const useBoardStore = defineStore('board', () => { return { ...apiBoard, id: String(apiBoard.id), // 确保 id 是字符串 - createdAt: new Date(apiBoard.createdAt), - updatedAt: new Date(apiBoard.updatedAt) + createdAt: new Date(apiBoard.created_at || apiBoard.createdAt), + updatedAt: new Date(apiBoard.updated_at || apiBoard.updatedAt) } } @@ -33,12 +33,14 @@ export const useBoardStore = defineStore('board', () => { * 从 API 获取看板列表 * 优化数据处理,减少不必要的对象转换 */ - const fetchBoards = async () => { + const fetchBoards = async (projectId?: string) => { loading.value = true error.value = null try { - const response = await boardApiService.getKanbanList() + const response = projectId + ? await boardApiService.getKanbanListByProject(projectId) + : await boardApiService.getKanbanList() // 验证响应数据 if (response.data && Array.isArray(response.data)) { @@ -67,8 +69,9 @@ export const useBoardStore = defineStore('board', () => { try { const response = await boardApiService.createKanban(board, projectId) - if (response.data) { - const newBoard = transformBoardData(response.data) + if (response.data || response.id) { + const boardData = response.data || response + const newBoard = transformBoardData(boardData) boards.value.push(newBoard) return newBoard } else { diff --git a/frontend/src/views/dashboard/BoardListView.vue b/frontend/src/views/dashboard/BoardListView.vue index 244c63b..f4c3d55 100644 --- a/frontend/src/views/dashboard/BoardListView.vue +++ b/frontend/src/views/dashboard/BoardListView.vue @@ -1,11 +1,20 @@