2022年最新《谷粒学院开发教程》:2 - 前后端交互篇

2022年最新《谷粒学院开发教程》:2 - 前后端交互篇,第1张

资料地址1 - 构建工程篇3 - 文件上传

目录 一、搭建前端工程1.1、测试启动1.2、目录结构1.3、配置环境 二、后台系统登录功能2.1、分析登录流程代码2.2、编写接口 三、讲师模块的前端实现3.1、路由视图及测试讲师列表接口3.2、渲染讲师列表3.3、分页功能实现3.4、多条件查询功能3.5、讲师删除3.6、讲师添加3.7、讲师修改-回显数据3.8、讲师修改3.9、bug解决


一、搭建前端工程 1.1、测试启动

1、从资料中解压 vue-element-admin 修改为 gulicollege_ui

2、安装依赖 npm install (安装太慢了直接放弃)

2、从资料中拷贝 node_modules 文件夹到项目中 并执行如下安装依赖

npm install node-sass

npm i node-sass -D

npm install --save element-ui

3、启动 npm run dev


1.2、目录结构
.
├── build 	// 构建脚本
├── config 	// 全局配置
├── node_modules // 项目依赖模块
├── src 	//项目源代码
├── static 	// 静态资源
└── package.jspon // 项目信息和依赖配置
src
├── api 		// 各种接口
├── assets 		// 图片等资源
├── components	// 各种公共组件,非公共组件在各自view下维护
├── icons 		//svg icon
├── router 		// 路由表
├── store 		// 存储
├── styles 		// 各种样式
├── utils 		// 公共工具,非公共工具,在各自view下维护
├── views 		// 各种layout
├── App.vue 	//***项目顶层组件***
├── main.js 	//***项目入口文件***
└── permission.js //认证入口

1.3、配置环境

1、package.json

{
  "name": "gulicollege_ui",
  "version": "3.8.0",
  "license": "MIT",
  "description": "谷粒学院后台管理系统",
  "author": "Laptoy",
}

2、config/index.js 配置端口号

port: 9528

3、config/index.js 关闭语法检查

useEslint: false

4、config/dev.env.js 修改为访问后台端口

BASE_API: '"http://localhost:8001"',

二、后台系统登录功能 2.1、分析登录流程代码

1、src/views/login/index.vue

methods: {
  handleLogin() {
    this.$refs.loginForm.validate(valid => {
      if (valid) {
        this.loading = true
        // 触发 store 的 Login事件
        this.$store.dispatch('Login', this.loginForm).then(() => {
          this.loading = false
          this.$router.push({ path: this.redirect || '/' })
        }).catch(() => {
          this.loading = false
        })
      } else {
        console.log('error submit!!')
        return false
      }
    })
  }
}

2、src/store/modules/user.js (这里没有使用 namespaces: true) 所以不需要通过命名空间调用

import { login, logout, getInfo } from '@/api/login'

actions: {
  // 登录
  Login({ commit }, userInfo) {
    const username = userInfo.username.trim()
    return new Promise((resolve, reject) => {
      login(username, userInfo.password).then(response => {
        const data = response.data
        setToken(data.token)
        commit('SET_TOKEN', data.token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },
}

3、src/api/login.js (通过 axios 发送请求)

import request from '@/utils/request'

export function login(username, password) {
  return request({
    url: '/user/login',
    method: 'post',
    data: {
      username,
      password
    }
  })
}

4、src/utils/request.js (封装 axios 调用)

import axios from 'axios'
import { Message, MessageBox } from 'element-ui'
import store from '../store'
import { getToken } from '@/utils/auth'

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api 的 base_url
  timeout: 5000 // 请求超时时间
})

// request拦截器
service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
    }
    return config
  },
  error => {
    // Do something with request error
    console.log(error) // for debug
    Promise.reject(error)
  }
)

// response 拦截器
service.interceptors.response.use(
  response => {
    /**
     * code为非20000是抛错 可结合自己业务进行修改
     */
    const res = response.data
    if (res.code !== 20000) {
      Message({
        message: res.message,
        type: 'error',
        duration: 5 * 1000
      })

      // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        MessageBox.confirm(
          '你已被登出,可以取消继续留在该页面,或者重新登录',
          '确定登出',
          {
            confirmButtonText: '重新登录',
            cancelButtonText: '取消',
            type: 'warning'
          }
        ).then(() => {
          store.dispatch('FedLogOut').then(() => {
            location.reload() // 为了重新实例化vue-router对象 避免bug
          })
        })
      }
      return Promise.reject('error')
    } else {
      return response.data
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

2.2、编写接口

根据前端代码分析

1、登录请求需要返回 token

2、获取用户信息请求需要返回 roles、name、avatar


1、EduLoginController (使用 @CrossOrigin 解决跨域问题)

@Api(tags = "登录模块")
@RestController
@CrossOrigin //解决跨域问题
@RequestMapping("/eduservice/user")
public class EduLoginController {

    // 登录
    @PostMapping("/login")
    public R login() {
        return R.ok().data("token", "admin");
    }

    // 获取信息
    @GetMapping("/info")
    public R info() {
        return R.ok()
                .data("roles", "admin")
                .data("name", "Laptoy")
                .data("avatar", "http://www.kaotop.com/file/tupian/20220516/480b7a82bddb4f6a9b6942c88db18d85.png");
    }

    // 登出
    @PostMapping("/logout")
    public R logOut() {
        return R.ok();
    }
}

2、修改 src/api/login.js 匹配后端接口


三、讲师模块的前端实现 3.1、路由视图及测试讲师列表接口

1、添加路由 - src/router/index.js

// 讲师模块路由
{
  path: '/teacher',
  component: Layout,
  redirect: '/teacher/table',
  name: '讲师管理',
  meta: { title: '讲师管理', icon: 'example' },
  children: [
    {
      path: 'table',
      name: '讲师列表',
      component: () => import('@/views/edu/teacher/list.vue'),
      meta: { title: '讲师列表', icon: 'table' }
    },
    {
      path: 'save',
      name: '添加讲师',
      component: () => import('@/views/edu/teacher/save.vue'),
      meta: { title: '添加讲师', icon: 'tree' }
    }
  ]
},

2、创建路由对应组件

创建文件夹 src/views/edu/teacher创建 list.vuesave.vue 组件

3、定义访问的接口地址

在src/api文件创建 teacher/teacher.js 定义访问的接口地址

import request from '@/utils/request'

export default {
  // 1、获取讲师列表(多条件分页查询)
  getTeacherListPage(page, limit, teacherQuery) {
    return request({
      url: `/eduservice/teacher/pageTeacherCondition/${page}/${limit}`,
      method: 'post',
      data: teacherQuery
    })
  }
}

4、组件页面调用定义接口方法 - list.vue

<template>
  <div class="app-container">讲师列表div>
template>

<script>
import teacher from '@/api/teacher/teacher.js'

export default {
  data() {
    return {
      list: null, // 查询之后给接口返回的数据装的集合
      page: 1, // 当前页
      limit: 5, // 每页显示记录数
      teacherQuery: {}, // 条件封装对象
      total: 0 // 总记录数
    }
  },
  created() {
    this.getList()
  },
  methods: {
    // 获取讲师列表
    getList() {
      teacher.getTeacherListPage(this.page, this.limit, this.teacherQuery)
        .then((resp) => {
          this.list = resp.data.data
          console.log(this.list)
          this.total = resp.data.total
          console.log(this.total)
        })
        .catch((err) => {
          console.log(err)
        })
    }
  }
}
script>

5、测试


3.2、渲染讲师列表

1、ui

<template>
  <div>
    <el-table :data="list" style="width: 100%" border fit highlight-current-row element-loading-text="数据加载中">
      <el-table-column prop="date" label="序号" width="70" align="center">
        <template slot-scope="scope">
          {{ (page - 1) * limit + scope.$index + 1 }}
        template>
      el-table-column>
      <el-table-column prop="name" label="名称" width="80" />
      <el-table-column label="头衔" width="80">
        <template slot-scope="scope">
          {{ scope.row.level === 1 ? "高级讲师" : "首席讲师" }}
        template>
      el-table-column>
      <el-table-column prop="intro" label="资历" />
      <el-table-column prop="gmtCreate" label="添加时间" width="160" />
      <el-table-column prop="sort" label="排序" width="60" />
      <el-table-column label=" *** 作" width="200" align="center">
        <template slot-scope="scope">
          <router-link :to="'/edu/teacher/edit/' + scope.row.id">
            <el-button type="primary" size="mini" icon="el-icon-edit">修改el-button>
          router-link>
          <el-button type="danger" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除el-button>
        template>
      el-table-column>
    el-table>
  div>
template>

2、展示


3.3、分页功能实现

1、ui

<div>
	<el-table>
	el-table>
	<el-pagination :total="total" :page-size="limit" :current-page="page" 
					background layout="prev, pager, next,total,jumper" 
					style="padding: 30px 0; text-align: center" 
					@current-change="getList" />
div>

2、方法

3、展示


3.4、多条件查询功能

1、ui


<el-form :inline="true" class="demo-form-inline" style="margin-left: 20px; margin-top: 12px;">
  <el-form-item label="名称">
    <el-input v-model="teacherQuery.name" placeholder="请输入名称">el-input>
  el-form-item>
  <el-form-item label="级别">
    <el-select v-model="teacherQuery.level" placeholder="讲师头衔">
      <el-option label="高级讲师" :value="1">el-option>
      <el-option label="特级讲师" :value="2">el-option>
    el-select>
  el-form-item>
  <el-form-item label="添加时间">
    <el-time-picker placeholder="选择开始时间" v-model="teacherQuery.begin" value-format="yyyy-MM-dd HH:mm:ss" default-time="00:00:00" type="datetime">el-form-item>
  <el-form-item>
    <el-time-picker placeholder="选择截止时间" v-model="teacherQuery.end" value-format="yyyy-MM-dd HH:mm:ss" default-time="00:00:00" type="datetime">el-time-picker>
  el-form-item>
  <el-form-item>
    <el-button type="primary" icon="el-icon-search" @click="getList()">查询el-button>
    <el-button type="default" @click="resetData()">清空el-button>
  el-form-item>
el-form>

2、方法

export default {
  methods: {
    // 清空查询栏
	resetData() {
	  // 表单输入项数据清空
	  this.teacherQuery = {};
	  // 查询所有讲师数据
	  this.getList();
	},
  }
}

3、展示


3.5、讲师删除

1、定义API - src/api/teacher/teacher.js

export default { 
  ...
  	
  // 2、根据id删除讲师
  removeById(id) {
    return request({
      url: `/eduservice/teacher/deleteTeacherById/${id}`,
      method: 'delete',
    })
  }
}

2、定义 methods

// 根据id删除讲师
removeDataById(id) {
  this.$confirm("此 *** 作将永久删除该讲师记录, 是否继续?", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    teacher.removeById(id)
      .then((resp) => {
        this.$message({
          type: "success",
          message: "删除成功!",
        });
        //刷新页面
        this.getList();
      });
  });
}

3.6、讲师添加

1、定义API

// 添加讲师
saveTeacher(teacher) {
  return request({
    url: `/eduservice/teacher/save`,
    method: `post`,
    data: teacher
  })
}

2、初始化 save.vue 组件

<template>
  <div class="app-container">
    <el-form label-width="120px">
      <el-form-item label="讲师名称">
        <el-input v-model="teacher.name" />
      el-form-item>
      <el-form-item label="讲师排序">
        <el-input-number v-model="teacher.sort" controls-position="right" :min="0" />
      el-form-item>
      <el-form-item label="讲师头衔">
        <el-select v-model="teacher.level" clearable placeholder="选择讲师头衔">
          <el-option :value="1" label="高级讲师" />
          <el-option :value="2" label="首席讲师" />
        el-select>
      el-form-item>
      <el-form-item label="讲师资历">
        <el-input v-model="teacher.career" placeholder="职业生涯、获得的证书、其他含金量的经历" />
      el-form-item>
      <el-form-item label="讲师简介">
        <el-input v-model="teacher.intro" :rows="3" type="textarea" />
      el-form-item>
      
      <el-form-item>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存el-button>
      el-form-item>
    el-form>
  div>
template>

<script>
//引入对应的新增api方法
import teacherApi from "@/api/teacher/teacher.js";

export default {
  data() {
    return {
      teacher: {
        name: "",
        sort: 0,
        level: 1,
        career: "",
        intro: "",
        avatar: "",
      },
      saveBtnDisabled: false, // 保存按钮是否禁用,
    };
  },
  methods: {
    saveOrUpdate() {
      this.saveBtnDisabled = true; // 防止表单多次提交,保存后就禁用按钮
      this.saveData();
    },
    // 保存
    saveData() {
      teacherApi.saveTeacher(this.teacher).then((resp) => {//添加成功
        //提示信息
        this.$message({
          message: "添加讲师记录成功",
          type: "success",
        });
        //回到讲师列表 路由跳转
        this.$router.push({ path: '/teacher/table' })
      });
    },
  },
};
script>

3、展示


3.7、讲师修改-回显数据

1、添加路由 - 实际上以保存组件作为模板跳转

// 讲师模块路由
{
  path: '/teacher',
  ...
  children: [
    ...
    {
      path: 'edit/:id',
      name: '修改讲师',
      component: () => import('@/views/edu/teacher/save.vue'),
      meta: { title: '修改讲师', noCache: true },
      hidden: true
    }
  ]
},

2、list.vue - 通过路由携带id进行修改

3、定义API 根据id查询信息

//根据id查询讲师
updateById(id) {
  return request({
    url: `/eduservice/teacher/getById/${id}`,
    method: `get`,
  })
}

4、根据id回显信息 - save.vue

// 修改请求--根据id回显信息
getInfoById(id) {
  teacherApi.updateById(id)
    .then(resp => {
      this.teacher = resp.data.data
    })
}

5、钩子函数添加判断

created() {
  // 判断路由跳转时是否携带参数
  if (this.$route.params && this.$route.params.id) {
    this.getInfoById(this.$route.params.id)
  }
}

6、展示 - 点击修改后数据回显


3.8、讲师修改

1、定义API

// 修改讲师信息
updateTeacherInfo(teacher) {
  return request({
    url: `/eduservice/teacher/updateById`,
    method: `post`,
    data: teacher
  })
}

2、修改方法 - save.vue

// 修改请求--修改讲师信息
updateTeacher() {
  teacherApi.updateTeacherInfo(this.teacher).then((resp) => {
    this.$message({
      message: "修改讲师记录成功",
      type: "success",
    });
    this.$router.push({ path: "/teacher/table" });
  });
}

3、区分新增或修改请求(通过路由跳转是否携带id进行判断)

saveOrUpdate() {
  if (!this.teacher.id) {
    //没有id值,做【新增 *** 作】
    this.saveBtnDisabled = true; // 只能点击一次保存 *** 作,防止表单多次保存
    this.saveData();
  } else {
    //有id值,做【修改 *** 作】
    this.updateTeacher()
  }
},

3.9、bug解决

场景

用户点击修改按钮后不想修改了反而想添加讲师点击添加讲师按钮,此时表单数据未清空
将 回显数据流程 封装为 init()方法(当路由中有id时回显数据,没有就清空表单)通过 watch属性 观察路由是否发生变化,变化那就重新执行 init() 进行页面加载

旧代码

created() {
  // 判断路由跳转时是否携带参数
  if (this.$route.params && this.$route.params.id) {
    this.getInfoById(this.$route.params.id)
  }
}

优化后

methods: {
  ...
  // 初始化页面
  init() {
    if (this.$route.params && this.$route.params.id) {
      this.getInfoById(this.$route.params.id)
    } else {
      this.teacher = {};
    }
  },
},
created() {
  this.init();
},
watch: {
  // 当路由发生变化时初始化页面
  $route(to, from) {
    this.init();
  }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/939797.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-17
下一篇 2022-05-17

发表评论

登录后才能评论

评论列表(0条)

保存