第二个简单的SpringBoot和Vue前后端全栈的todoapp案例

发布于:2025-05-11 ⋅ 阅读:(19) ⋅ 点赞:(0)

项目源于哔哩哔哩,按视频手敲了一下,补充上代码和一些细节。

全栈项目实践:1小时快速入门SpringBoot+vue3+element-plus_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LH4y1w7Nd/?spm_id_from=333.1387.favlist.content.click&vd_source=3d07f741ba02111ffca810ad6ade218a

H2数据库的安装和使用

  1. H2 Database Enginehttps://h2database.com/html/main.html 官网下载安装版文件h2-setup-2024-08-11.exehttps://github.com/h2database/h2database/releases/download/version-2.3.232/h2-setup-2024-08-11.exe
  2. IDEA配置H2,名称填写test时,会自动生成test.mv.db数据库文件,注意URL的写法,测试连接成功则配置成功
    jdbc:h2:file:C:/Users/Administrator/Desktop/todoapp/todoapp/test
  3. 数据库使用SQL语言建表Todo
    -- auto-generated definition
    create table TODO
    (
        ID        BIGINT auto_increment
            primary key,
        TITLE     CHARACTER VARYING(256),
        COMPLETED BOOLEAN
    );

  4. 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
  5. 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')


网站公告

今日签到

点亮在社区的每一天
去签到