构建全栈项目管理系统(Vue + Spring Boot)
本文介绍了一个基于Vue3+SpringBoot的项目管理系统架构设计。系统采用前后端分离架构,前端使用Vue3+ElementPlus实现项目列表、任务看板等功能页面,后端采用SpringBoot2.7+提供RESTful API,数据库使用MySQL8.0存储项目、任务、用户等数据。文章详细展示了后端数据库设计、JPA实体类、SpringSecurity配置和API控制器代码,以及前端Vue3
系统架构设计
项目管理系统架构
├── 前端 (Vue 3 + Element Plus)
│ ├── 项目列表与详情
│ ├── 任务看板
│ ├── 团队管理
│ └── 数据统计
├── 后端 (Spring Boot 2.7+)
│ ├── 用户认证模块
│ ├── 项目管理模块
│ ├── 任务管理模块
│ └── 团队管理模块
└── 数据库 (MySQL 8.0)
├── 用户表
├── 项目表
├── 任务表
└── 团队表
后端实现 (Spring Boot)
1. 项目结构与依赖
首先使用Spring Initializr创建项目,主要依赖:
- Spring Web
- Spring Data JPA
- MySQL Driver
- Spring Security
- JJWT (JSON Web Token)
2. 数据库设计
使用AI工具生成数据库SQL脚本:
CREATE DATABASE project_management;
USE project_management;
-- 用户表
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
display_name VARCHAR(100),
role ENUM('ADMIN', 'MANAGER', 'MEMBER') DEFAULT 'MEMBER',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 项目表
CREATE TABLE projects (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
description TEXT,
status ENUM('PLANNING', 'IN_PROGRESS', 'ON_HOLD', 'COMPLETED') DEFAULT 'PLANNING',
start_date DATE,
end_date DATE,
owner_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES users(id)
);
-- 任务表
CREATE TABLE tasks (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
description TEXT,
status ENUM('TODO', 'IN_PROGRESS', 'REVIEW', 'DONE') DEFAULT 'TODO',
priority ENUM('LOW', 'MEDIUM', 'HIGH') DEFAULT 'MEDIUM',
assignee_id BIGINT,
project_id BIGINT NOT NULL,
due_date DATE,
estimated_hours INT,
actual_hours INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (assignee_id) REFERENCES users(id),
FOREIGN KEY (project_id) REFERENCES projects(id)
);
-- 项目成员关联表
CREATE TABLE project_members (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
project_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
role ENUM('OWNER', 'MANAGER', 'MEMBER') DEFAULT 'MEMBER',
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES projects(id),
FOREIGN KEY (user_id) REFERENCES users(id),
UNIQUE KEY unique_member (project_id, user_id)
);
3. 核心Java实体类
使用AI工具生成JPA实体类:
// User实体
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Column(nullable = false, unique = true)
private String email;
private String displayName;
@Enumerated(EnumType.STRING)
private UserRole role = UserRole.MEMBER;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
public enum UserRole {
ADMIN, MANAGER, MEMBER
}
}
// Project实体
@Entity
@Table(name = "projects")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
private String description;
@Enumerated(EnumType.STRING)
private ProjectStatus status = ProjectStatus.PLANNING;
private LocalDate startDate;
private LocalDate endDate;
@ManyToOne
@JoinColumn(name = "owner_id", nullable = false)
private User owner;
@OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
private List<Task> tasks = new ArrayList<>();
@OneToMany(mappedBy = "project", cascade = CascadeType.ALL)
private List<ProjectMember> members = new ArrayList<>();
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
public enum ProjectStatus {
PLANNING, IN_PROGRESS, ON_HOLD, COMPLETED
}
}
4. Spring Security配置
使用AI工具生成JWT认证配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/projects/**").authenticated()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
5. RESTful API控制器
使用AI工具生成Project控制器:
@RestController
@RequestMapping("/api/projects")
public class ProjectController {
@Autowired
private ProjectService projectService;
@GetMapping
public ResponseEntity<List<ProjectDTO>> getUserProjects(
@AuthenticationPrincipal User user,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Page<ProjectDTO> projects = projectService.getUserProjects(user, page, size);
return ResponseEntity.ok()
.header("X-Total-Count", String.valueOf(projects.getTotalElements()))
.body(projects.getContent());
}
@PostMapping
public ResponseEntity<ProjectDTO> createProject(
@RequestBody ProjectRequest request,
@AuthenticationPrincipal User user) {
ProjectDTO project = projectService.createProject(request, user);
return ResponseEntity.status(HttpStatus.CREATED).body(project);
}
@GetMapping("/{id}")
public ResponseEntity<ProjectDTO> getProject(
@PathVariable Long id,
@AuthenticationPrincipal User user) {
ProjectDTO project = projectService.getProject(id, user);
return ResponseEntity.ok(project);
}
@PutMapping("/{id}")
public ResponseEntity<ProjectDTO> updateProject(
@PathVariable Long id,
@RequestBody ProjectRequest request,
@AuthenticationPrincipal User user) {
ProjectDTO project = projectService.updateProject(id, request, user);
return ResponseEntity.ok(project);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProject(
@PathVariable Long id,
@AuthenticationPrincipal User user) {
projectService.deleteProject(id, user);
return ResponseEntity.noContent().build();
}
}
前端实现 (Vue 3)
1. 项目初始化与依赖
使用Vue CLI创建项目并安装依赖:
vue create project-management-frontend
cd project-management-frontend
npm install axios vue-router vuex element-plus
2. 路由配置
使用AI工具生成Vue Router配置:
import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'
import Dashboard from '../views/Dashboard.vue'
import Projects from '../views/Projects.vue'
import ProjectDetail from '../views/ProjectDetail.vue'
const routes = [
{
path: '/',
redirect: '/dashboard'
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: { requiresAuth: true }
},
{
path: '/projects',
name: 'Projects',
component: Projects,
meta: { requiresAuth: true }
},
{
path: '/projects/:id',
name: 'ProjectDetail',
component: ProjectDetail,
meta: { requiresAuth: true }
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
// 导航守卫
router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('authToken')
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
} else if (to.name === 'Login' && isAuthenticated) {
next('/dashboard')
} else {
next()
}
})
export default router
3. Vuex状态管理
使用AI工具生成Vuex store:
import { createStore } from 'vuex'
import axios from 'axios'
const API_BASE_URL = 'http://localhost:8080/api'
export default createStore({
state: {
user: null,
token: localStorage.getItem('authToken') || null,
projects: []
},
mutations: {
setUser(state, user) {
state.user = user
},
setToken(state, token) {
state.token = token
if (token) {
localStorage.setItem('authToken', token)
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
} else {
localStorage.removeItem('authToken')
delete axios.defaults.headers.common['Authorization']
}
},
setProjects(state, projects) {
state.projects = projects
},
addProject(state, project) {
state.projects.push(project)
},
updateProject(state, updatedProject) {
const index = state.projects.findIndex(p => p.id === updatedProject.id)
if (index !== -1) {
state.projects.splice(index, 1, updatedProject)
}
}
},
actions: {
async login({ commit }, credentials) {
try {
const response = await axios.post(`${API_BASE_URL}/auth/login`, credentials)
commit('setToken', response.data.token)
commit('setUser', response.data.user)
return response.data
} catch (error) {
throw error.response.data
}
},
async fetchProjects({ commit }) {
try {
const response = await axios.get(`${API_BASE_URL}/projects`)
commit('setProjects', response.data)
return response.data
} catch (error) {
throw error.response.data
}
},
async createProject({ commit }, projectData) {
try {
const response = await axios.post(`${API_BASE_URL}/projects`, projectData)
commit('addProject', response.data)
return response.data
} catch (error) {
throw error.response.data
}
}
},
getters: {
isAuthenticated: state => !!state.token,
currentUser: state => state.user,
allProjects: state => state.projects,
projectById: state => id => state.projects.find(project => project.id === id)
}
})
4. 项目列表组件
使用AI工具生成Projects.vue组件:
<template>
<div class="projects-container">
<div class="header">
<h1>项目管理</h1>
<el-button type="primary" @click="showCreateDialog = true">
<i class="el-icon-plus"></i> 新建项目
</el-button>
</div>
<el-table :data="projects" v-loading="loading">
<el-table-column prop="name" label="项目名称" min-width="200">
<template #default="scope">
<router-link :to="`/projects/${scope.row.id}`" class="project-link">
{{ scope.row.name }}
</router-link>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="startDate" label="开始日期" width="120">
<template #default="scope">
{{ formatDate(scope.row.startDate) }}
</template>
</el-table-column>
<el-table-column prop="endDate" label="结束日期" width="120">
<template #default="scope">
{{ formatDate(scope.row.endDate) }}
</template>
</el-table-column>
<el-table-column prop="owner.displayName" label="负责人" width="120"></el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="mini" @click="editProject(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="deleteProject(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 创建项目对话框 -->
<el-dialog title="新建项目" v-model="showCreateDialog" width="500px">
<project-form
@submit="createProject"
@cancel="showCreateDialog = false">
</project-form>
</el-dialog>
<!-- 编辑项目对话框 -->
<el-dialog title="编辑项目" v-model="showEditDialog" width="500px">
<project-form
:project="editingProject"
@submit="updateProject"
@cancel="showEditDialog = false">
</project-form>
</el-dialog>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import ProjectForm from '@/components/ProjectForm.vue'
export default {
name: 'Projects',
components: {
ProjectForm
},
data() {
return {
loading: false,
showCreateDialog: false,
showEditDialog: false,
editingProject: null
}
},
computed: {
...mapState(['projects'])
},
async created() {
this.loading = true
try {
await this.fetchProjects()
} catch (error) {
this.$message.error('获取项目列表失败')
} finally {
this.loading = false
}
},
methods: {
...mapActions(['fetchProjects', 'createProject', 'updateProject', 'deleteProject']),
getStatusType(status) {
const statusMap = {
'PLANNING': 'info',
'IN_PROGRESS': 'primary',
'ON_HOLD': 'warning',
'COMPLETED': 'success'
}
return statusMap[status] || 'info'
},
getStatusText(status) {
const statusTextMap = {
'PLANNING': '规划中',
'IN_PROGRESS': '进行中',
'ON_HOLD': '已暂停',
'COMPLETED': '已完成'
}
return statusTextMap[status] || status
},
formatDate(date) {
if (!date) return '-'
return new Date(date).toLocaleDateString()
},
editProject(project) {
this.editingProject = { ...project }
this.showEditDialog = true
}
}
}
</script>
<style scoped>
.projects-container {
padding: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.project-link {
color: #409EFF;
text-decoration: none;
}
.project-link:hover {
text-decoration: underline;
}
</style>
5. 项目表单组件
使用AI工具生成ProjectForm.vue组件:
<template>
<el-form :model="form" :rules="rules" ref="projectForm" label-width="80px">
<el-form-item label="项目名称" prop="name">
<el-input v-model="form.name" placeholder="请输入项目名称"></el-input>
</el-form-item>
<el-form-item label="项目描述" prop="description">
<el-input
type="textarea"
:rows="3"
v-model="form.description"
placeholder="请输入项目描述">
</el-input>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态">
<el-option
v-for="status in statusOptions"
:key="status.value"
:label="status.label"
:value="status.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="开始日期" prop="startDate">
<el-date-picker
v-model="form.startDate"
type="date"
placeholder="选择开始日期">
</el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker
v-model="form.endDate"
type="date"
placeholder="选择结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="cancel">取消</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'ProjectForm',
props: {
project: {
type: Object,
default: null
}
},
data() {
return {
form: {
name: '',
description: '',
status: 'PLANNING',
startDate: null,
endDate: null
},
rules: {
name: [
{ required: true, message: '请输入项目名称', trigger: 'blur' },
{ min: 2, max: 100, message: '长度在 2 到 100 个字符', trigger: 'blur' }
]
},
statusOptions: [
{ value: 'PLANNING', label: '规划中' },
{ value: 'IN_PROGRESS', label: '进行中' },
{ value: 'ON_HOLD', label: '已暂停' },
{ value: 'COMPLETED', label: '已完成' }
]
}
},
watch: {
project: {
immediate: true,
handler(newVal) {
if (newVal) {
this.form = { ...newVal }
}
}
}
},
methods: {
submitForm() {
this.$refs.projectForm.validate((valid) => {
if (valid) {
this.$emit('submit', this.form)
} else {
return false
}
})
},
cancel() {
this.$emit('cancel')
}
}
}
</script>
使用AI编程工具的提示词示例
在开发过程中,可以使用以下提示词来辅助AI编程工具生成代码:
-
生成数据库实体:
“使用Java Spring Boot创建一个Project实体类,包含id、名称、描述、状态、开始日期、结束日期、负责人等字段,使用JPA注解” -
生成API控制器:
“创建一个Spring Boot REST控制器,实现项目的CRUD操作,包括分页查询和权限检查” -
生成Vue组件:
“创建一个Vue 3组件,使用Element Plus显示项目列表表格,包含名称、状态、日期等列,并提供编辑和删除按钮” -
生成表单验证:
“为项目创建表单添加验证规则,确保名称为必填且长度在2-100字符之间”
部署与运行
后端部署
- 配置MySQL数据库连接
- 使用Maven构建项目:
mvn clean package
- 运行JAR文件:
java -jar target/project-management.jar
前端部署
- 配置API基础URL
- 构建生产版本:
npm run build
- 部署到Nginx或其它Web服务器
总结
通过使用AI编程工具,我们可以快速生成项目管理系统的基础代码框架。这个系统包含了用户认证、项目管理、任务管理等核心功能。AI工具帮助我们:
- 快速生成数据库设计和实体类
- 自动创建RESTful API控制器
- 生成前端Vue组件和状态管理代码
- 提供表单验证和用户界面设计
实际开发中,您可以根据具体需求进一步扩展功能,如添加文件上传、实时通知、甘特图视图、时间跟踪等高级功能。使用AI编程工具可以显著提高开发效率,减少重复性编码工作,让开发者更专注于业务逻辑和用户体验优化。
更多推荐
所有评论(0)