SpringBoot + Vue + Element 前后端分離的管理后台項目簡單入門


這篇文章講講如何寫一個簡單的前后端分離的項目。技術棧用到了 Springboot 、 Vue 、element-UI 整合了 Mybatis 做一個管理后台數據的增刪改查。

一.准備工作

① 環境搭建
  1. 安裝 node.js,可以默認安裝以及自定義安裝(自行選擇),配置系統環境變量。由於新版的 node.js 已經集成了 npm,所以 npm 也一並安裝好了。

node.js 官方下載鏈接

菜鳥教程安裝配置 node.js

安裝完畢后,打開 cmd.exe 分別輸入下面的命令查看是否安裝成功,若出現版本號則安裝成功!

node -v
npm -v

image

  1. 安裝 vue 腳手架工具

首先安裝npm的淘寶鏡像(可選,下載速度比較快,推薦下載)

輸入以下的命令

npm i -g cnpm --registry=https://registry.npm.taobao.org

輸入cnpm -v ,不報錯即安裝成功!

image

注意:出現cnpm 是不是內部或者外部命令,不要着急,配置電腦環境變量將 node.js 安裝路徑中 x:../xxx/node_global 文件夾路徑復制到 path 中即可

接着安裝 vue 腳手架工具:

cnpm i -g vue-cli

cmd.exe 測試是否安裝成功:

vue -V

  1. 安裝 webpack 前端資源加載/打包工具。

在 cmd.exe 命令行執行:

cnpm install webpack -g

② 項目創建

這里我創建了一個空的 maven 項目,然后把 pom.xml 文件和 src 目錄刪除:

然后點擊左上角的 file => New => module 創建一個 springboot 后端模塊,項目結構如下所示:

image

接下來創建 vue 前端模塊

點擊 idea 下方 Termainal 打開終端控制台(等效於 cmd.exe),輸入 vue init webpack 你的項目名

image

然后一路回車確認,這里主要是配置 vue 項目的一些配置,大家可以根據需要自行選擇,具體內容介紹我放在下面:

  • Project name (baoge): -----項目名稱,直接回車,按照括號中默認名字(注意這里的名字不能有大寫字母,如果有會報錯Sorry, name can no longer contain capital letters)

  • Project description (A Vue.js project): ----項目描述,也可直接點擊回車,使用默認名字

  • Author (): ----作者,輸入你的大名

  • untime + Compiler: recommended for most users 運行加編譯,既然已經說了推薦,就選它了

  • Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specificHTML) are ONLY allowed in .vue files - render functions are required elsewhere 僅運行時,已經有推薦了就選擇第一個了

  • Install vue-router? (Y/n) 是否安裝vue-router,這是官方的路由,大多數情況下都使用,這里就輸入 “y” 后回車即可。

  • Use ESLint to lint your code? (Y/n) 是否使用ESLint管理代碼,ESLint是個代碼風格管理工具,是用來統一代碼風格的,這里我選 “n” 后回車。

  • 接下來也是選擇題Pick an ESLint preset (Use arrow keys) 選擇一個ESLint預設,編寫vue項目時的代碼風格,這里我選 “n” 后回車。

  • Setup unit tests with Karma + Mocha? (Y/n) 是否安裝單元測試,這里我選 “n” 后回車。

  • Setup e2e tests with Nightwatch(Y/n)? 是否安裝e2e測試 ,這里我選 “n” 后回車。

image

接着只需等待即可,此時項目結構變成了這樣:

image

生成好后,可以看到 Terminal 控制台輸出:

image

我們需要根據提示,在命令行依次輸入

  cd vue-ui
  cnpm install
  npm run dev

此時可以看到提示 vue 程序啟動成功,並且給出了前端頁面訪問路徑

image

根據路徑訪問 http://localhost:8080

image

成功訪問到了 vue 項目,此時前端項目就已經成功跑起來了!

二.功能實現

上面講到了前端項目的搭建,這段主要寫后端提供具體的接口以及前端項目如何調用后台接口

首先到 mysql 數據庫新建一個 student 表

CREATE TABLE `student`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名字',
  `age` int(11) NULL DEFAULT NULL COMMENT '年齡',
  `gender` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性別',
  `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '郵箱',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '學生表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, '張三', 15, '男', '111@qq.com');
INSERT INTO `student` VALUES (2, '李四', 16, '女', '222@qq.com');
INSERT INTO `student` VALUES (3, '王五', 14, '男', '333@qq.com');
INSERT INTO `student` VALUES (4, '趙六', 15, '男', '444@qq.com');
INSERT INTO `student` VALUES (5, '錢七', 13, '女', '555@qq.com');

SET FOREIGN_KEY_CHECKS = 1;
① 后端接口准備

到 pom.xml 添加需要的 jar 包


	<!-- springboot web -->
 	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        ​
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

修改 springboot 配置,因為前面 vue項目使用了 8080 端口,所以我們把 SpringBoot項目的端口改成 9090,以及添加數據庫配置,我的 Mysql 數據庫是 5.7.17 版本,大家根據自己數據庫版本導入 maven 依賴和配置

# 應用名稱
spring.application.name=vue-module
# 應用服務 WEB 訪問端口
server.port=9090

# mysql數據庫連接
spring.datasource.username=你的數據庫賬號
spring.datasource.password=你的數據庫密碼
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/數據庫名?useUnicode=true&&useSSL=false&&characterEncoding=UTF-8&&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true

# mybatis
mybatis.mapper-locations=classpath: mybatis/*.xml mybatis xml文件存放地址
mybatis.type-aliases-package=com.lin.entity 你的實體地址

image

接着在 java 目錄下創建四層架構包,resources 目錄下創建 mybatis 文件夾用於存放 mapper.xml 文件

  • controller 控制層:負責調用 service 層接口
  • entity 實體層:實體類是屬性對象,用於供給其他層引用,該層的屬性往往和數據庫的表結構各個字段相同
  • mapper 數據層:mapper 層所定義的接口要實現對數據的操作
  • service 業務邏輯層: 負責功能的編寫,將所需要的功能都定義在一個 service 層的接口當中,

接下來的部分內容比較多,我就直接貼 crud 的代碼不放截圖了,大家也可以自己寫

entity

然后先從實體類開始,一一對應數據庫字段,這里因為只是 demo 以及業務邏輯不復雜所以沒有細分領域模型,如有需要可以自行分類

package com.lin.entity;

public class Student {

 /**
     * id
     */
    private Integer id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年齡
     */
    private Integer age;


    /**
     * 性別
     */
    private String gender;

    /**
     * 郵箱
     */
    private String email;

//這里省略 get、set、toString 方法,實在太多了沒意義,自行補上即可
}

mapper 接口

package com.lin.mapper;
import com.lin.entity.Student;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;

@Mapper
public interface StudentMapper {

    /**
     * 查詢全部 Student 數據
     * @return List<Student>
     */
    List<Student> listStudent();

    /**
     * 根據 id 查詢一條 Student 數據
     * @param id
     * @return Student
     */
    Student selectStudentById(Integer id);

    /**
     * 新增一條 Student 數據
     * @return
     */
    int insertStudent(Student student);

    /**
     * 根據 id 刪除一條 Student 數據
     * @return
     */
    int deleteStudentById(Integer id);

    /**
     * 更新一條 Student 數據
     * @return
     */
    int updateStudent(Student student);
}

mapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.lin.mapper.StudentMapper">

    <resultMap type="com.lin.entity.Student" id="StudentResult">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="gender" column="gender"/>
        <result property="email" column="email"/>
    </resultMap>

    <sql id="selectStudent">
        select `id`,`name`,`age`,`gender`,`email` from student
    </sql>

    <select id="listStudent" resultMap="StudentResult">
        <include refid="selectStudent"></include>
    </select>

    <select id="selectStudentById" resultMap="StudentResult">
        <include refid="selectStudent"></include>
        where `id` = #{id};
    </select>

    <insert id="insertStudent" parameterType="com.lin.entity.Student" useGeneratedKeys="true">
        insert into student
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="name != null and name != ''">`name`,</if>
            <if test="age != null and age != ''">`age`,</if>
            <if test="gender != null and gender != ''">`gender`,</if>
            <if test="email != null and email != ''">`email`,</if>
        </trim>
        <trim prefix="values(" suffix=")" suffixOverrides=",">
            <if test="name != null and name != ''">#{name},</if>
            <if test="age != null and age != ''">#{age},</if>
            <if test="gender != null and gender != ''">#{gender},</if>
            <if test="email != null and email != ''">#{email},</if>
        </trim>
    </insert>

    <delete id="deleteStudentById" parameterType="integer">
        delete from student where `id` = #{id}
    </delete>

    <update id="updateStudent" parameterType="com.lin.entity.Student">
        update student
        <trim prefix="set" suffixOverrides=",">
            <if test="name != null and name != ''">`name` = #{name},</if>
            <if test="age != null and age != ''">`age` = #{age},</if>
            <if test="gender != null and gender != ''">`gender` = #{gender},</if>
            <if test="email != null and email != ''">`email` = #{email}</if>
        </trim>
        where `id` = #{id}
    </update>
</mapper>

service 接口:

package com.lin.service;
import com.lin.entity.Student;
import java.util.List;


public interface IStudentService{

    /**
     * 查詢全部 Student 數據
     * @return List<Student>
     */
    List<Student> listStudent();

    /**
     * 根據 id 查詢一條 Student 數據
     * @param id
     * @return Student
     */
    Student selectStudentById(Integer id);

    /**
     * 新增一條 Student 數據
     * @return
     */
    int insertStudent(Student student);

    /**
     * 根據 id 刪除一條 Student 數據
     * @return
     */
    int deleteStudentById(Integer id);

    /**
     * 更新一條 Student 數據
     * @return
     */
    int updateStudent(Student student);

}

serviceImpl

package com.lin.service.impl;
import com.lin.entity.Student;
import com.lin.mapper.StudentMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.lin.service.IStudentService;
import java.util.List;

@Service
public class StudentServiceImpl implements IStudentService{
    @Autowired
    StudentMapper studentMapper;

    /**
     * 查詢全部 Student 數據
     * @return List<Student>
     */
    @Override
    public List<Student> listStudent() {
        return studentMapper.listStudent();
    }

    /**
     * 根據 id 查詢一條 Student 數據
     * @param id
     * @return Student
     */
    @Override
    public Student selectStudentById(Integer id) {
        return studentMapper.selectStudentById(id);
    }

    /**
     * 新增一條 Student 數據
     * @param student
     * @return
     */
    @Override
    public int insertStudent(Student student) {
        return studentMapper.insertStudent(student);
    }

    /**
     * 根據 id 刪除一條 Student 數據
     * @param id
     * @return
     */
    @Override
    public int deleteStudentById(Integer id) {
        return studentMapper.deleteStudentById(id);
    }

    /**
     * 更新一條 Student 數據
     * @param student
     * @return
     */
    @Override
    public int updateStudent(Student student) {
        return studentMapper.updateStudent(student);
    }
}

controller:

package com.lin.controller;
import com.lin.entity.Student;
import com.lin.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/student")
public class StudentController {
    @Autowired
    IStudentService iStudentService;

    /**
     * 獲取全部學生數據接口
     * @return List<Student>
     */
    @GetMapping(value = "/info",produces = {"application/json;charset=UTF-8"})
    public List<Student> listStudentInfo(){
        return iStudentService.listStudent();
    }

    /**
     * 根據 id 查詢一條 Student 數據接口
     * @param id
     * @return Student
     */
    @GetMapping(value = "/info/{id}",produces = {"application/json;charset=UTF-8"})
    public Student studentInfoById(@PathVariable(value = "id")Integer id){
        return iStudentService.selectStudentById(id);
    }

    /**
     * 新增一條 Student 數據
     * @param student
     */
    @PostMapping(value = "/add",produces = {"application/json;charset=UTF-8"})
    public void addStudent(@RequestBody Student student){
        iStudentService.insertStudent(student);
    }

    /**
     * 刪除一條 Student 數據
     * @param id
     */
    @PostMapping(value = "/remove/{id}",produces = {"application/json;charset=UTF-8"})
    public void removeStudent(@PathVariable("id") Integer id){
        iStudentService.deleteStudentById(id);
    }

    /**
     * 修改一條 Student 數據
     * @param student
     */
    @PostMapping(value = "/edit",produces = {"application/json;charset=UTF-8"})
    public void editStudent(@RequestBody Student student){
        iStudentService.updateStudent(student);
    }
}

寫好后項目路徑如下所示:

接口用 PostMan 測試正常,此時后端接口也准備完畢!

② 前端頁面准備

接下來開始繼續前端頁面編寫,估計看博客的你們也都是后端開發,所以前端頁面估計也寫得不太好看,好在有許多優秀的 UI 組件可以使用。這里我們使用 Element 組件庫

組件庫安裝可以使用 npm 或 cdn安裝(推薦 npm )

在 Termainal 終端控制台輸入 cnpm i element-ui -S 后回車,稍等即可

image

修改 vue 項目根目錄 main.js 添加下面三行即可完成 element 的安裝

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

image

接着開始新建一個新的 vue 頁面

左上角 File => Settings 點擊 Plugins 輸入 Vue.js 並安裝
image

在 vue 項目 src 目錄新建一個 views 文件夾用於存放頁面,右鍵點擊 views 文件夾 new => Vue Conponent,然后在 template 標簽里面寫點東西

 <div>
    <p>Hello,world!</p>
    <el-row>
      <el-button>默認按鈕</el-button>
      <el-button type="primary">主要按鈕</el-button>
      <el-button type="success">成功按鈕</el-button>
      <el-button type="info">信息按鈕</el-button>
      <el-button type="warning">警告按鈕</el-button>
      <el-button type="danger">危險按鈕</el-button>
    </el-row>
  </div>

image

接着修改路由,在 router 目錄下 index.js 文件中添加 login 路徑

import login from '@/views/login'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/login',
      name: 'login',
      component: login
    }
  ]
})

image

然后到瀏覽器輸入 http://localhost:8080/#/login 就可以看到效果了!

image

這個 vue 的 logo 我們不需要,所以到項目根目錄把 App.vue 中 template 標簽中的 <img src="./assets/logo.png"> 給注釋掉,刷新就會發現圖標不見了,全局的 style 標簽的內容不需要的話可以注釋掉,然后添加以下代碼減去 body 自身的邊距

<style>
  body {
    padding: 0;
    margin: 0;
  }
  html,body,#app {
    height: 100%;
  }
</style>

image

注意:正常前端項目修改后會自動重新編譯,如果沒有自動編譯得到效果 按 ctrl + c 關閉項目后重新執行 npm run dev 重新編譯項目即可

上面測試完頁面路由以及 element 的引入后,我們需要把 login 頁做成一個真正的登陸頁面,所以修改代碼如下:
login.vue:

<template>
  <div class="login-container">
    <el-form :model="ruleForm2" :rules="rules2"
             status-icon
             ref="ruleForm2"
             label-position="left"
             label-width="0px"
             class="demo-ruleForm login-page">
      <h3 class="title">系統登錄</h3>
      <el-form-item prop="username">
        <el-input type="text"
                  v-model="ruleForm2.username"
                  auto-complete="off"
                  placeholder="用戶名"
        ></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input type="password"
                  v-model="ruleForm2.password"
                  auto-complete="off"
                  placeholder="密碼"
        ></el-input>
      </el-form-item>
      <el-checkbox
        v-model="checked"
        class="rememberme"
      >記住密碼
      </el-checkbox>
      <el-form-item style="width:100%;">
        <el-button type="primary" style="width:100%;" @click="handleSubmit" :loading="logining">登錄</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
  export default {
    name: "login",
    data(){
      return {
        logining: false,
        ruleForm2: {
          username: 'admin',
          password: '123456',
        },
        rules2: {
          username: [{required: true, message: 'please enter your account', trigger: 'blur'}],
          password: [{required: true, message: 'enter your password', trigger: 'blur'}]
        },
        checked: false
      }
    },
    methods: {
      handleSubmit(event){
        this.$refs.ruleForm2.validate((valid) => {
          if(valid){
            this.logining = true;
            if(this.ruleForm2.username === 'admin' &&
              this.ruleForm2.password === '123456'){
              this.logining = false;
              sessionStorage.setItem('user', this.ruleForm2.username);
              this.$router.push({path: '/index'});
            }else{
              this.logining = false;
              this.$alert('username or password wrong!', 'info', {
                confirmButtonText: 'ok'
              })
            }
          }else{
            console.log('error submit!');
            return false;
          }
        })
      }
    }
  }
</script>

<style scoped>

  .login-container {
    width: 100%;
    height: 100%;
  }
  .login-page {
    -webkit-border-radius: 5px;
    border-radius: 5px;
    width: 350px;
    padding: 35px 35px 15px;
    background: #fff;
    border: 1px solid #eaeaea;
    box-shadow: 0 0 25px #cac6c6;
    margin: 0;
    position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }

  label.el-checkbox.rememberme {
    margin: 0px 0px 15px;
    text-align: left;
  }

  .title{
    text-align: center;
  }
</style>

效果如下:
image

登陸賬號:admin 密碼:123456
這個頁面是我在網上簡單找了個登陸頁面 源碼地址

然后接着創建一個登陸后跳轉的頁面 index.vue,我就不贅述了,跟上面創建 login 頁面一樣,然后引入 element 布局容器,就可以出現一個后台系統的布局框架了

index.vue 布局容器:

<template>
  <el-container>
    <el-aside width="200px">Aside</el-aside>
    <el-container>
      <el-header>Header</el-header>
      <el-main>Main</el-main>
      <el-footer>Footer</el-footer>
    </el-container>
  </el-container>
</template>

<script>
  export default {
    name: "index",
  }
</script>

<style scoped>
  .el-header, .el-footer {
    background-color: #B3C0D1;
    color: #333;
    text-align: center;
    line-height: 60px;
    height:100%
  }

  .el-aside {
    background-color: #D3DCE6;
    color: #333;
    text-align: center;
    line-height: 100vh;
  }

  .el-main {
    background-color: #E9EEF3;
    color: #333;
    text-align: center;
    line-height: 160px;
  }
</style>

image

然后在 Aside 標簽里面引入 element 側邊欄,修改樣式,並且加上一點點細節

index.vue 側邊欄:

<template>
  <el-container>
    <!-- width的寬度跟collapse一樣動態控制 -->
    <el-aside width="collapse">
      <div class="logo" v-show="open"><h3><i class="el-icon-eleme"></i>xxx管理系統</h3></div>
      <div class="logo" v-show="close"><h3><i class="el-icon-eleme"></i></h3></div>
      <!-- :collapse="isCollapse"  class="el-menu-vertical" 動態控制導航菜單的收起與展開  router:讓index作為 path 進行路由跳轉 -->
      <el-menu default-active="$route.path"
               router
               :default-openeds="openeds"
               :collapse="isCollapse"
               class="el-menu-vertical">
        <el-submenu index="1">
          <!-- 一級標題 -->
          <template slot="title">
            <i class="el-icon-s-tools"></i>
            <span slot="title">后台管理</span>
          </template>
          <!--  二級標題 -->
            <el-menu-item index="/console">
              <i class="el-icon-setting"></i>
              <span slot="title">控制台</span>
            </el-menu-item>
            <el-menu-item index="/student">
              <i class="el-icon-setting"></i>
              <span slot="title">學生管理</span>
            </el-menu-item>
        </el-submenu>
      </el-menu>

    </el-aside>
    <el-container>
      <el-header>
        <div class="trigger" @click="isShow">
          <!-- 點擊展開收起導航和切換對應圖標 -->
          <i class="el-icon-s-fold" v-show="open"></i>
          <i class="el-icon-s-unfold" v-show="close"></i>
        </div>
      </el-header>
      <el-main>
        <router-view></router-view>
      </el-main>
      <el-footer>Footer</el-footer>
    </el-container>
  </el-container>
</template>

<script>
  export default {
    name: "index",
    data() {
      return {
        openeds: ["1"],
        isCollapse: false, //導航欄默認為展開
        close: false, //第二個圖標默認隱藏
        open: true, //默認顯示第一個圖標
      }
    },
    methods: {
      isShow() {
        this.isCollapse = !this.isCollapse;
        this.open = !this.open;
        this.close = !this.close;
      },
    }
  }
</script>

<style scoped>
  .el-header, .el-footer {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
    height: 100%;
    padding: 0 !important;
  }

  .el-aside {
    background-color: #D3DCE6;
    color: #333;
    height: 100vh;
  }

  .el-main {
    background-color: #E9EEF3;
    color: #333;
  }

  body > .el-container {
    margin-bottom: 40px;
  }

  .logo {
    height: 60px;
    line-height: 60px;
    background-color: antiquewhite;
    text-align: center;
  }

  .logo h3 {
    margin: 0;
    height: 60px;
  }

  .el-menu {
    border-right-width: 0;
  }

  .el-menu-vertical:not(.el-menu--collapse) {
    width: 240px;
  }

  .trigger {
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    width: 54px;
  }

  .trigger i {
    font-size: 20px;
  }

  .trigger:hover {
    background-color: rgb(203, 215, 230);
  }
</style>

效果如下:

展開:

收起:

中間 main 部分我加了個 <router-view></router-view> 目的是做一個路由嵌套,就是我們的控制台、學生管理頁面將會被渲染在這里面

接下里創建 console 、student 頁面:

image

然后到 router 文件夾下的 index.js 修改路由路徑

import console from '@/views/console';
import student from '@/views/student';

 {
      path: '/index',
      name: 'index',
      component: index,
      children:[
        {
          path: '/console',
          name: 'console',
          component: console
        },
        {
          path: '/student',
          name: 'student',
          component: student
        }
      ]
    }

image

將 index 頁面路徑重定向到 console 頁面,children 聲明子路由,這就完成了路由嵌套,到前端刷新頁面,看到成功渲染 console 頁面,
點擊學生管理,也一樣可以出現效果,這里就不展示了

image

接着我們修改 student 頁面,引入 element 表格,對話框等控件,然后我們要考慮如何跟后台做交互,vue 推薦的是 axios 而不是 ajax。

首先要明白的是axios是什么:axios是基於promise(諾言)用於 node.js 和瀏覽器中。axios的作用是什么呢:axios主要是用於向后台發起請求的,還有在請求中做更多是可控功能。

我們在 Terminal 終端控制台輸入 cnpm install axios --save-dev

然后到根目錄下修改 main.js 完成掛載

import axios from 'axios'
Vue.prototype.$axios = axios

new Vue({
  axios
})

image

axios 的語法我也不詳細說明了,基本上也都是填寫那幾個屬性,想詳細了解可以到 axios文檔 看看

下面修改 student 頁面,加入表格,彈出表單和一些按鈕

<template>
  <el-card class="box-card">
    <!-- Dialog 對話框 彈出新增和修改表單 -->
    <el-row>
      <el-button size="mini" type="primary" @click="add">新增</el-button>
      <el-dialog :title="title" :visible.sync="dialogFormVisible" width="30%">
        <el-form :model="form" :rules="rules" ref="form">
          <el-form-item label="id:" hidden>
            <el-input v-model="form.id"></el-input>
          </el-form-item>
          <el-form-item label="姓名:" prop="name">
            <el-input v-model="form.name" placeholder="請輸入姓名" style="width:80%"></el-input>
          </el-form-item>
          <el-form-item label="年齡:" prop="age">
            <el-input v-model.number="form.age" placeholder="請輸入年齡" style="width:80%"></el-input>
          </el-form-item>
          <el-form-item label="性別:" prop="gender">
            <el-select v-model="form.gender" placeholder="請選擇性別" style="width:80%">
              <el-option label="男" value="男"></el-option>
              <el-option label="女" value="女"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="郵箱:" prop="email">
            <el-input v-model="form.email" placeholder="請輸入郵箱" style="width:80%"></el-input>
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button @click="dialogFormVisible = false">取 消</el-button>
          <el-button type="primary" @click="submit()">提 交</el-button>
        </div>
      </el-dialog>
    </el-row>

    <!-- 表格 -->
    <el-table
      ref="singleTable"
      :data="tableData"
      style="width: 100%">
      <el-table-column
        type="selection"
        width="55">
      </el-table-column>
      <el-table-column
        property="id"
        label="ID"
        width="50"
        align="center">
      </el-table-column>
      <el-table-column
        property="name"
        label="姓名"
        width="120"
        align="center">
      </el-table-column>
      <el-table-column
        property="age"
        label="年齡"
        width="120"
        align="center">
      </el-table-column>
      <el-table-column
        property="gender"
        label="性別"
        width="120"
        align="center">
      </el-table-column>
      <el-table-column
        property="email"
        label="郵箱"
        align="center">
      </el-table-column>
      <el-table-column label="操作" align="center">
        <template slot-scope="scope">
          <el-button
            size="mini"
            @click="edit(scope.$index, scope.row)">編輯
          </el-button>
          <el-button
            size="mini"
            type="danger"
            @click="remove(scope.$index, scope.row)">刪除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-card>
</template>

<script>
  export default {
    name: "student",
    data() {
      return {
        title: '',
        currentRow: null,
        dialogFormVisible: false,
        form: {},
        tableData: [],
        rules: {
          name: [{required: true, message: '請輸入姓名', trigger: 'blur'}],
          age: [{required: true, message: '請輸入年齡', trigger: 'blur'},
            {type: 'number', message: '年齡必須為數字值', trigger: 'blur'},
            {pattern: /^(0|[1-9]\d?|200)$/, message: '范圍在0-200', trigger: 'blur'}],
          gender: [{required: true, message: '請選擇性別', trigger: 'change'}],
          email: [{required: true, message: '請輸入郵箱', trigger: 'blur'}]
        }
      }
    },
    methods: {
      // 表單重置初始化
      reset() {
        this.form = {
          id: null,
          name: null,
          age: null,
          gender: null,
          email: null
        }
      },

      // 增
      add() {
        this.reset()
        this.dialogFormVisible = true
        this.title = "新增學生數據"
      },

      // 刪
      remove(index, row) {
        console.log(row.id)
        this.$axios({
          method: 'post',
          url: 'http://localhost:9090/student/remove/' + row.id,
        }).then((response) => {
          this.$message({
            message: '刪除成功!',
            type: 'success'
          });
          this.getList();
        }).catch((error) => {
        })
      },

      // 改
      edit(index, row) {
        this.reset()
        this.form = JSON.parse(JSON.stringify(row));
        this.dialogFormVisible = true
        this.title = "修改學生數據"
      },

      //查
      getList() {
        this.$axios({
          method: 'get',
          url: 'http://localhost:9090/student/info',
        }).then((response) => {
          this.tableData = response.data
        }).catch((error) => {
        })
      },

      //提交按鈕
      submit() {
        this.$refs['form'].validate((valid) => {
          if (valid) {
            if (this.form.id == null) {
              this.$axios({
                method: 'post',
                data: this.form,
                url: 'http://localhost:9090/student/add',
              }).then((response) => {
                this.$message({
                  message: '新增成功!',
                  type: 'success'
                });
                this.dialogFormVisible = false
                this.getList();
              }).catch((error) => {
              })
            } else {
              this.$axios({
                method: 'post',
                data: this.form,
                url: 'http://localhost:9090/student/edit',
              }).then((response) => {
                this.$message({
                  message: '修改成功!',
                  type: 'success'
                });
                this.getList();
                this.dialogFormVisible = false
              }).catch((error) => {
              })
            }
          } else {
            return false;
          }
        })
      }
    },
    mounted() {
      this.getList();
    }
  }
</script>

<style scoped>
</style>

注意: 因為我們前端項目是 8080 端口,后台項目是 9090端口,此時必然會遇到一個問題 " 跨域 "

什么是跨域

所謂同源(即指在同一個域)就是兩個頁面具有相同的協議(protocol),主機(host)端口號(port)

同源策略是瀏覽器的一個安全功能,不同源的客戶端腳本在沒有明確授權的情況下,不能讀寫對方資源。 同源策略是瀏覽器安全的基石

同源策略會阻止一個域的 javascript 腳本和另外一個域的內容進行交互。例如辦公內外網環境,當我們訪問外網一個惡意網站的時候,惡意網站就會利用我們的主機向內網的 url 發送 ajax 請求,破壞或盜取數據

當一個請求url的協議、域名、端口三者之間任意一個與當前頁面url不同即為跨域:

當前頁面url 被請求頁面url 是否跨域 原因
http://www.test.com/ http://www.test.com/index.html 同源(協議、域名、端口號相同)
http://www.test.com/ https://www.test.com/index.html 跨域 協議不同(http/https)
http://www.test.com/ http://www.baidu.com/ 跨域 主域名不同(test/baidu)
http://www.test.com/ http://blog.test.com/ 跨域 子域名不同(www/blog)
http://www.test.com:8080/ http://www.test.com:7001/ 跨域 端口號不同(8080/7001)

跨域可以在前端解決也可以在后端解決,解決方法也挺多種的,這里我偷懶就到 springboot 后台用注解解決

在控制層 StudentController 類上加一個注解 @CrossOrigin(origins = "*")即可

點擊新增或修改按鈕彈出對話框表單

什么都先不填,直接點擊提交,會發現表單輸入框下有一行提示,因為我們上面的代碼用了 element 表單驗證,當然也可以自定義表單驗證,這個可以非常友好的提示用戶哪些選項為必填項,減少輸入出錯的情況。后台有需要也可以做一些判空操作,這樣新增、編輯數據出錯的情況就會減少

填入數據,點擊提交

image

成功新增數據,編輯也同樣道理,我就不演示了

image

點擊刪除,同樣也可以刪除成功!

總結

這樣簡單的前后端分離的增、刪、改、查后台業務就完成了,但是還是有很多不完善的地方,很多地方都可以統一封裝成組件等,以后我也會以這個項目為基礎繼續往上完善!

未完待續....


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM