第二个简单的SpringBoot和Vue前后端全栈的todoapp案例
项目源于哔哩哔哩,按视频手敲了一下,补充上代码和一些细节。
全栈项目实践:1小时快速入门SpringBoot+vue3+element-plus_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LH4y1w7Nd/?spm_id_from=333.1387.favlist.content.click&vd_source=3d07f741ba02111ffca810ad6ade218a
H2数据库的安装和使用
- H2 Database Engine
https://h2database.com/html/main.html 官网下载安装版文件h2-setup-2024-08-11.exe
https://github.com/h2database/h2database/releases/download/version-2.3.232/h2-setup-2024-08-11.exe
- IDEA配置H2,名称填写test时,会自动生成test.mv.db数据库文件,注意URL的写法,测试连接成功则配置成功
jdbc:h2:file:C:/Users/Administrator/Desktop/todoapp/todoapp/test
- 数据库使用SQL语言建表Todo
-- auto-generated definition create table TODO (ID BIGINT auto_incrementprimary key,TITLE CHARACTER VARYING(256),COMPLETED BOOLEAN );
- application.properties定义好H2数据库
spring.h2.console.enabled=true spring.datasource.url=jdbc:h2:file:C:/Users/Administrator/Desktop/todoapp/todoapp/test spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
-
SpringBoot先网络测试要出现200成功
其他的根据视频来就行,没要到视频代码,自己手敲一份,附上代码部分。
package com.example.todoapp.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowedMethods("*").allowedHeaders("*");}
}
package com.example.todoapp.controller;import com.example.todoapp.domain.Todo;
import com.example.todoapp.service.TodoService;
import jakarta.annotation.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/api/todos")
public class TodoController {@Resourceprivate TodoService todoService;@GetMappingpublic List<Todo> getAllTodos() {return todoService.getAllTodos();}@PostMappingpublic Todo createTodo(@RequestBody Todo todo) {return todoService.createTodo(todo);}@PutMapping("/{id}")public Todo updateTodo(@PathVariable Long id, @RequestBody Todo updatedTodo) {return todoService.updateTodo(id, updatedTodo);}@DeleteMapping("/{id}")public void deleteTodo(@PathVariable Long id){todoService.deleteTodo(id);}
}
package com.example.todoapp.dao;import com.example.todoapp.domain.Todo;
import org.springframework.data.jpa.repository.JpaRepository;public interface TodoRepository extends JpaRepository<Todo, Long> {
}
package com.example.todoapp.domain;import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;@Entity
public class Todo {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String title;private Boolean completed;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public Boolean getCompleted() {return completed;}public void setCompleted(Boolean completed) {this.completed = completed;}@Overridepublic String toString() {return "Todo{" +"id=" + id +", title='" + title + '\'' +", completed=" + completed +'}';}
}
package com.example.todoapp.service;import com.example.todoapp.domain.Todo;import java.util.List;public interface TodoService {List<Todo> getAllTodos();Todo createTodo(Todo todo);Todo updateTodo(Long id,Todo updatedTodo);void deleteTodo(Long id);
}
package com.example.todoapp.service;import com.example.todoapp.dao.TodoRepository;
import com.example.todoapp.domain.Todo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class TodoServiceImpl implements TodoService {@Resourceprivate TodoRepository todoRepository;@Overridepublic List<Todo> getAllTodos() {return todoRepository.findAll();}@Overridepublic Todo createTodo(Todo todo) {return todoRepository.save(todo);}@Overridepublic Todo updateTodo(Long id, Todo updatedTodo) {return todoRepository.findById(id).map(todo -> {todo.setTitle(updatedTodo.getTitle());todo.setCompleted(updatedTodo.getCompleted());return todoRepository.save(todo);}).orElseGet(() -> {updatedTodo.setId(id);return todoRepository.save(updatedTodo);});}@Overridepublic void deleteTodo(Long id) {todoRepository.deleteById(id);}
}
<script setup>
import { onMounted, ref } from 'vue'
import axios from 'axios'
import { ElMessage } from 'element-plus'const newTodo = ref('')const todos = ref([])const axiosInstance = axios.create({baseURL: 'http://localhost:8080',timeout: 5000,
})const fetchTodos = async () => {try {const response = await axiosInstance.get('/api/todos')todos.value = response.data} catch (error) {ElMessage.error('查询待办事项失败')console.error(error)}
}const addTodo = async () => {if (!newTodo.value) returntry {const response = await axiosInstance.post('/api/todos', {title: newTodo.value,completed: false,})todos.value.push(response.data)newTodo.value = ''ElMessage.success('待办事项创建成功')} catch (error) {ElMessage.error('待办事项创建失败')console.error(error)}
}const toggleCompleted = async (todo) => {try {todo.completed = !todo.completedawait axiosInstance.put(`/api/todos/${todo.id}`, todo)ElMessage.success('待办事项更新成功')} catch (error) {ElMessage.error('待办事项更新失败')console.error(error)todo.completed = !todo.completed}
}const deleteTodo = async (todo) => {try {await axiosInstance.delete(`/api/todos/${todo.id}`)await fetchTodos()ElMessage.success('待办事项更新成功')} catch (error) {ElMessage.error('待办事项更新失败')console.error(error)todo.completed = !todo.completed}
}onMounted(fetchTodos)
</script><template><div class="todo-app"><el-card class="todo-card"><template #header><div class="todo-header">待办事项</div></template><div class="todo-input"><el-input v-model="newTodo" placeholder="新建待办事项..."></el-input><el-button type="primary" @click="addTodo">添加</el-button></div><div v-if="todos.length" class="todo-list"><el-card v-for="todo in todos" :key="todo.id" class="todo-item"><div class="todo-item-actions"><div class="todo-item-title">{{ todo.title }}</div><el-buttonclass="todo-button"@click="toggleCompleted(todo)":type="todo.completed ? 'success' : 'info'">{{ todo.completed ? '已完成' : '未完成' }}</el-button><el-button type="danger" @click="deleteTodo(todo)">删除</el-button></div></el-card></div><div v-else class="no-todos">暂无待办事项</div></el-card></div>
</template><style scoped>
.todo-app {display: flex;justify-self: center;align-items: center;height: 100vh;background: #f0f2f5;padding: 20px;box-sizing: border-box;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
.todo-card {width: 100%;max-width: 500px;border-radius: 8px;box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.1);background: #ffffff;
}
.todo-header {font-size: 24px;font-weight: bold;text-align: center;padding: 16px;background-color: #409eff;color: #fff;border-radius: 8px 8px 0 0;margin: 0;
}
.todo-input {display: flex;align-items: center;gap: 10px;padding: 20px;background-color: #fff;border-bottom: 1px solid #ebeef5;
}
.el-input {flex: 1;
}
.todo-list {padding: 20px;background-color: #fff;
}
.todo-item {display: flex;justify-content: space-between;align-items: center;padding: 10px 15px;border: 1px solid #ebeef5;border-radius: 8px;background-color: #f9f9f9;transition:background-color 0.3s,transform 0.3s;
}
.todo-item:hover {background-color: #ebf7ff;tranform: translateY(-2px);
}
.todo-item-title {font-weight: bold;flex: 1;margin-right: 20px;word-wrap: break-word;width: 160px;
}
.completed .todo-item-title {text-decoration: line-through;color: #909399;
}
.todo-item-actions {display: flex;align-items: center;
}
.no-todos {text-align: center;padding: 20px;color: #909399;font-size: 18px;
}
</style>
<script setup>
import TodoList from './components/TodoList.vue';
</script><template><div id="app"><el-container><el-main><TodoList/></el-main></el-container></div>
</template>
import { createApp } from 'vue'
import App from './App.vue'
import 'element-plus/dist/index.css'
import ElementPlus from 'element-plus'const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')