项目源于哔哩哔哩,按视频手敲了一下,补充上代码和一些细节。
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_increment primary 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 {
@Override
public 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 {
@Resource
private TodoService todoService;
@GetMapping
public List<Todo> getAllTodos() {
return todoService.getAllTodos();
}
@PostMapping
public 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;
}
@Override
public 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 {
@Resource
private TodoRepository todoRepository;
@Override
public List<Todo> getAllTodos() {
return todoRepository.findAll();
}
@Override
public Todo createTodo(Todo todo) {
return todoRepository.save(todo);
}
@Override
public 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);
});
}
@Override
public 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) return
try {
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.completed
await 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-button
class="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')