Spring Boot+Vue 前后端分离项目架构

项目流程:

1. SpringBoot 后端项目

1、新建一个 SpringBoot 工程,并添加项目开发过程中需要的相关依赖;


2、数据库新建 book 数据表;

-- ------------------------------ Table structure for book-- ----------------------------DROP TABLE IF EXISTS `book`;CREATE TABLE `book`  (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `title` varchar(55) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,  `publish` varchar(55) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,  `authors` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,  `createTime` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),  `updateTime` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of book-- ----------------------------INSERT INTO `book` VALUES (2, '数据结构', '浙江出版社', '严威', '2022-03-18 14:38:22', '2022-03-18 14:38:25');INSERT INTO `book` VALUES (5, '计算机组成原理', '机械工业出版社', '唐朔飞', '2022-03-14 14:38:28', '2022-03-15 14:38:36');INSERT INTO `book` VALUES (6, '机器学习', '人民邮电出版社', '周志华', '2022-03-16 14:38:41', '2022-03-16 14:38:45');INSERT INTO `book` VALUES (7, '图论及其应用', '科学技术出版社', '徐俊明', '2022-03-03 14:38:49', '2022-03-18 18:33:57');INSERT INTO `book` VALUES (8, '推荐系统开发实战', '电子工业出版社', '高团阳', '2022-03-14 14:38:57', '2022-03-16 14:39:02');INSERT INTO `book` VALUES (9, '大数据导论', '机械工业出版社', '曾诚', '2022-03-10 14:39:06', '2022-03-12 14:39:09');INSERT INTO `book` VALUES (13, 'Java从零基础到实战', '中国工信出版集团', '宁楠', '2022-03-18 17:05:18', '2022-03-18 17:05:18');

3、application.yml 配置数据源和项目启动的端口号;

# 修改服务启动的端口server:  port: 8181spring:  datasource:    url: jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=UTF-8    username: root    password: 123456    driver-class-name: com.mysql.jdbc.Driver# 配置日志,打印执行的sql语句mybatis-plus:  configuration:    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

项目整体的目录结构如下:

4、创建实体类 Book.java;

@AllArgsConstructor@NoArgsConstructor@Data@TableName(value = "book")public class Book {    @TableId(type = IdType.AUTO)    private Long id;    private String title;    private String publish;    private String authors;    @TableField(value = "createTime", fill = FieldFill.INSERT)    private Date createTime;    @TableField(value = "updateTime", fill = FieldFill.INSERT_UPDATE)    private Date updateTime;}

5、添加 MyBatis Plus 自动填充的处理器 config/DataHandler.java;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@Componentpublic class DataHandler implements MetaObjectHandler{    //插入时的填充策略    @Override    public void insertFill(MetaObject metaObject) {        this.setFieldValByName("createTime", new Date(), metaObject);        this.setFieldValByName("updateTime", new Date(), metaObject);    }    //更新时的填充策略    @Override    public void updateFill(MetaObject metaObject) {        this.setFieldValByName("updateTime", new Date(), metaObject);    }}

6、持久化层 mapper/BookMapper.java;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.trainingl.entity.Book;import org.springframework.stereotype.Repository;@Repository  //持久层注解public interface BookMapper extends BaseMapper<Book> {    //所有的CRUD操作都已经编写好了}

7、服务层 service;

service/BookService.java

import com.trainingl.entity.Book;import java.util.List;//数据的增删改查接口public interface BookService {    public List<Book> findAll();    public Book findById(Long id);    public int insert(Book book);    public int deleteById(Long id);    public int updateById(Book book);}

service/Impl/BookServiceImpl.java

import com.trainingl.entity.Book;import com.trainingl.mapper.BookMapper;import com.trainingl.service.BookService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class BookServiceImpl implements BookService {    @Autowired    private BookMapper bookMapper;    @Override    public List<Book> findAll() {        //查询搜索记录        return bookMapper.selectList(null);    }    @Override    public Book findById(Long id) {        return bookMapper.selectById(id);    }    @Override    public int insert(Book book) {        return bookMapper.insert(book);    }    @Override    public int deleteById(Long id) {        return bookMapper.deleteById(id);    }    @Override    public int updateById(Book book) {        return bookMapper.updateById(book);    }}

8、控制层 Controller;

controller/BookController.java

import com.trainingl.entity.Book;import com.trainingl.service.BookService;import org.apache.ibatis.annotations.Param;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import java.util.HashMap;import java.util.List;import java.util.Map;@RestController@CrossOrigin  //跨域注解public class BookController {    @Autowired    private BookService bookService;    @RequestMapping("/booklist")    public List<Book> booklist(){        return bookService.findAll();    }    @RequestMapping("/addbook")    public int addbook(@RequestBody Book book){        return bookService.insert(book);    }    @DeleteMapping("/deleteById/{id}")    public int deleteById(@PathVariable("id") Long id){        Integer result = 0;        result = bookService.deleteById(id);        //删除成功则返回受影响的行数        return result;    }    @GetMapping("/findById/{id}")    public Book findById(@PathVariable("id") Long id){        return bookService.findById(id);    }    @PutMapping("/updateById")    public int updateById(@RequestBody Book book){        //返回修改的条数        return bookService.updateById(book);    }}

2. Vue 前端项目

1、主页面 App.vue

<template>    <div id="app">        <router-view/>    </div></template><script>    export default {        name: 'App',        components: {        },        data(){            return{            }        },    }</script>

2、数据列表视图 views/table.vue

<template>    <div>        <el-table                :data="tableData"                border                style="width: 70%">            <el-table-column                    fixed                    prop="id"                    label="编号"                    width="80">            </el-table-column>            <el-table-column                    prop="title"                    label="书籍名称"                    width="160">            </el-table-column>            <el-table-column                    prop="publish"                    label="出版社"                    width="150">            </el-table-column>            <el-table-column                    prop="authors"                    label="作者"                    width="120">            </el-table-column>            <el-table-column                    prop="createTime"                    label="创建时间"                    width="150">            </el-table-column>            <el-table-column                    prop="updateTime"                    label="修改时间"                    width="150">            </el-table-column>            <el-table-column                    fixed="right"                    label="操作"                    width="150">                <template slot-scope="scope">                    <el-button @click="del(scope.row)" type="danger" size="small">删除</el-button>                    <el-button @click="edit(scope.row)" type="success" size="small">编辑</el-button>                </template>            </el-table-column>        </el-table>        <br>        <el-button type="primary" @click="addBook" icon="el-icon-edit" size="small">添加记录</el-button>    </div></template><script>    export default {        name: "table",        methods: {            addBook(){                this.$router.push("/addbook")            },            del(row) {                let _this = this;                this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {                    confirmButtonText: '确定',                    cancelButtonText: '取消',                    type: 'warning'                }).then(() => {                    this.$axios.delete("http://localhost:8181/deleteById/" + row.id).then(function (response) {                        if (response.data == 1) {                            //刷新页面                            location.reload();                        } else {                            _this.$message({                                showClose: true,                                message: '删除信息失败!',                                type: 'danger'                            });                        }                    })                })            },            edit(row) {                this.$router.push("/edit?id=" + row.id);            }        },        data() {            return {                tableData: []            }        },        created(){            let _this = this;            this.$axios.get("http://localhost:8181/booklist").then(function (response) {                _this.tableData = response.data;            })        }    }</script><style scoped></style>

3、新增视图文件 views/addBook.vue

<template>    <div>        <h2 style="padding-left: 200px">新增图书信息</h2>        <el-form :model="ruleForm" style="width: 500px;" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">            <el-form-item label="书籍名称" prop="title">                <el-input type="text" v-model="ruleForm.title" autocomplete="off"></el-input>            </el-form-item>            <el-form-item label="出版社" prop="publish">                <el-input type="text" v-model="ruleForm.publish" autocomplete="off"></el-input>            </el-form-item>            <el-form-item label="作者信息" prop="authors">                <el-input v-model.number="ruleForm.authors"></el-input>            </el-form-item>            <el-form-item>                <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>                <el-button @click="resetForm('ruleForm')">重置</el-button>            </el-form-item>        </el-form>    </div></template><script>    export default {        name: "addBook",        data() {            var checkAuthor = (rule, value, callback) => {                if (value === '') {                    callback(new Error('请输入作者信息'));                } else {                    callback();                }            };            var validateTitle = (rule, value, callback) => {                if (value === '') {                    callback(new Error('请输入书籍名称'));                } else {                    callback();                }            };            var validatePublish = (rule, value, callback) => {                if (value === '') {                    callback(new Error('请输入出版社'));                } else {                    callback();                }            };            return {                ruleForm: {                    title: '',                    publish: '',                    authors: ''                },                rules: {                    title: [                        { validator: validateTitle, trigger: 'blur' }                    ],                    publish: [                        { validator: validatePublish, trigger: 'blur' }                    ],                    authors: [                        { validator: checkAuthor, trigger: 'blur' }                    ]                }            };        },        methods: {            submitForm(formName) {                let _this = this;                this.$refs[formName].validate((valid) => {                    if (valid) {                        this.$axios.post("http://localhost:8181/addbook", _this.ruleForm).then(function (response) {                            if(response.data == 1){                                //跳转到表格页面                                _this.$router.push('/table')                            }                        })                    } else {                        console.log('error submit!!');                        return false;                    }                });            },            resetForm(formName) {                this.$refs[formName].resetFields();            }        }    }</script><style scoped></style>

4、编辑或修改视图文件 views/edit.vue

<template>    <div>        <h2 style="padding-left: 200px">修改图书信息</h2>        <el-form :model="ruleForm" style="width: 500px;" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">            <el-form-item label="图书编号">                <el-input type="text" readonly  v-model="ruleForm.id" autocomplete="off"></el-input>            </el-form-item>            <el-form-item label="书籍名称" prop="title">                <el-input type="text" v-model="ruleForm.title" autocomplete="off"></el-input>            </el-form-item>            <el-form-item label="出版社" prop="publish">                <el-input type="text" v-model="ruleForm.publish" autocomplete="off"></el-input>            </el-form-item>            <el-form-item label="作者信息" prop="authors">                <el-input v-model.number="ruleForm.authors"></el-input>            </el-form-item>            <el-form-item label="创建时间">                <el-input readonly v-model.number="ruleForm.createTime"></el-input>            </el-form-item>            <el-form-item label="修改时间">                <el-input readonly v-model.number="ruleForm.updateTime"></el-input>            </el-form-item>            <el-form-item>                <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>                <el-button @click="resetForm('ruleForm')">重置</el-button>            </el-form-item>        </el-form>    </div></template><script>    export default {        name: "edit",        data() {            var checkAuthor = (rule, value, callback) => {                if (value === '') {                    callback(new Error('请输入作者信息'));                } else {                    callback();                }            };            var validateTitle = (rule, value, callback) => {                if (value === '') {                    callback(new Error('请输入书籍名称'));                } else {                    callback();                }            };            var validatePublish = (rule, value, callback) => {                if (value === '') {                    callback(new Error('请输入出版社'));                } else {                    callback();                }            };            return {                ruleForm: {                    id: '',                    title: '',                    publish: '',                    authors: '',                    createTime: '',                    updateTime: ''                },                rules: {                    title: [                        { validator: validateTitle, trigger: 'blur' }                    ],                    publish: [                        { validator: validatePublish, trigger: 'blur' }                    ],                    authors: [                        { validator: checkAuthor, trigger: 'blur' }                    ]                }            };        },        methods: {            submitForm(formName) {                let _this = this;                this.$refs[formName].validate((valid) => {                    if (valid) {                        this.$axios.put("http://localhost:8181/updateById", _this.ruleForm).then(function (response) {                            if(response.data === 1){                                //跳转到表格页面                                _this.$router.push('/table')                            }                        })                    } else {                        console.log('error submit!!');                        return false;                    }                });            },            resetForm(formName) {                this.$refs[formName].resetFields();            }        },        mounted(){            let _this = this;            this.$axios.get("http://localhost:8181/findById/" + this.$route.query.id).then(function (response) {                _this.ruleForm = response.data;            });        }    }</script><style scoped></style>

5、路由文件 router.js

import Vue from 'vue'//1.导入路由依赖,安装 vue-router组件后系统带有的依赖import VueRouter from 'vue-router'//2.导入两个页面import Login from "@/views/login";import Second from "@/views/Second";import Table from "@/views/table";import AddBook from "@/views/addBook";import Edit from "@/views/edit";// 3.使用路由Vue.use(VueRouter);//4.创建路由对象,new VueRouter为上面import设置的名称const router = new VueRouter({    //没有history,访问URL会有#    mode: "history",    routes: [        {            path:"/login",            component: Login        },        {            path:"/second",            component: Second        },        {            path:"/table",            component: Table        },        {            path:"/addbook",            component: AddBook        },        {            path:"/edit",            component: Edit        },    ]});export default router;

6、组件的全局配置 main.js

import Vue from 'vue'import App from './App.vue'import router from "./router/index"import ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';import axios from "axios"import qstring from "querystring"// Vue 全局Vue对象,prototype原型设置全局属性Vue.prototype.$axios = axiosVue.prototype.$qstring=qstringVue.config.productionTip = false;Vue.use(ElementUI);new Vue({    router,    render: h => h(App),}).$mount('#app')