Vue实战之使用Vue CLI 脚手架开发前后端分离项目之员工列表实现


案例演示

在这里插入图片描述

创建数据库

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50731
 Source Host           : localhost:3306
 Source Schema         : helloworld

 Target Server Type    : MySQL
 Target Server Version : 50731
 File Encoding         : 65001

 Date: 12/08/2021 05:51:29
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of admin
-- ----------------------------
INSERT INTO `admin` VALUES (1, 'zhangsan', '123456');
INSERT INTO `admin` VALUES (2, 'xiaozhu', '123');

-- ----------------------------
-- Table structure for emp
-- ----------------------------
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  `salary` double(10, 2) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES (1, '小张1', 23, 23000.00);
INSERT INTO `emp` VALUES (2, '小李2', 24, 2400.00);
INSERT INTO `emp` VALUES (3, '小白', 25, 2500.00);
INSERT INTO `emp` VALUES (4, '小王', 25, 1500.00);
INSERT INTO `emp` VALUES (5, '小红红', 24, 1500.00);
INSERT INTO `emp` VALUES (6, '小四', 25, 1500.00);

SET FOREIGN_KEY_CHECKS = 1;

创建后端项目

pom.xml 文件中引入依赖坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.baizhi</groupId>
    <artifactId>helloworld_back</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.5</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

        <!--引入redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
application.properties
server.port=8989

#配置mybatis相关
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/helloworld?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root

mybatis.type-aliases-package=com.baizhi.entity
mybatis.mapper-locations=classpath:com/baizhi/mapper/*.xml

#配置redis
spring.redis.host=127.0.0.1
spring.rabbitmq.host=6379

logging.level.com.baizhi.dao=debug
实现用户登录、用户退出登录功能
  • com.baizhi.constants.RedisPrefix(常量)
public interface RedisPrefix {
    String TOKEN = "TOKEN_";
}
  • com.baizhi.controller.AdminController(完成用户登录、退出登录功能)
@RestController
@CrossOrigin //允许跨域
public class AdminController {
    private static final Logger log = LoggerFactory.getLogger(AdminController.class);
    /**
     * 服务对象
     */
    @Resource
    private AdminService adminService;

    @Autowired
    private RedisTemplate redisTemplate;

    //退出用户登录
    @DeleteMapping("/token")
    public void token(String token) {
        redisTemplate.delete(RedisPrefix.TOKEN + token);
    }

    //已登录用户信息(获取token信息)
    @GetMapping("/token")
    public Admin admin(String token) {
        log.info("接收token信息:{} ", token);
        return (Admin) redisTemplate.opsForValue().get(RedisPrefix.TOKEN + token);
    }

    //用户登录
    @PostMapping("login")
    public Map<String, Object> login(@RequestBody Admin admin, HttpSession session) {
        Map<String, Object> result = new HashMap<>();
        log.info("admin username:{} password:{}", admin.getUsername(), admin.getPassword());
        try {
            //登录成功
            Admin adminDB = adminService.login(admin);
            //生成token信息
            String token = session.getId();
            //放入redis
            redisTemplate.opsForValue().set(RedisPrefix.TOKEN + token, adminDB, 30, TimeUnit.MINUTES);
            result.put("success", true);
            result.put("token", token);
        } catch (Exception e) {
            e.printStackTrace();
            result.put("success", false);
            result.put("msg", e.getMessage());
        }
        return result;
    }

    /**
     * 通过主键查询单条数据
     */
    @GetMapping("/admin")
    public Admin admin(Integer id) {
        return this.adminService.queryById(id);
    }
}
  • com.baizhi.service.AdminService
public interface AdminService {

	// ... ...
	
    //管理员登录
    Admin login(Admin admin);
}
  • com.baizhi.service.impl.AdminServiceImpl
@Service("adminService")
@Transactional
public class AdminServiceImpl implements AdminService {
    @Resource
    private AdminDao adminDao;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public Admin login(Admin admin) {
        Admin adminDB = adminDao.findByUserName(admin.getUsername());
        if (adminDB == null) {
            throw new RuntimeException("用户名输入错误!!");
        }
        if (!adminDB.getPassword().equals(admin.getPassword())) {
            throw new RuntimeException("密码输入错误!!!");
        }
        return adminDB;
    }
}
  • com.baizhi.dao.AdminDao
public interface AdminDao {

	// ... ...
	
    //根据用户名查询用户
    Admin findByUserName(String username);
}
  • com.baizhi.mapper.AdminDao.xml
<select id="findByUserName" parameterType="string" resultType="Admin">
    select id, username, password
    from admin
    where username = #{username}
</select>
使用 Vue Cli 创建前端项目
  • 项目结构

在这里插入图片描述

  • helloworld\src\utils\request.js
//引入axios
import axios from "axios";

//创建默认实例
const instance = axios.create({
  baseURL: 'http://localhost:8989',
  timeout: 5000,
})

//请求拦截器
instance.interceptors.request.use(config => {
  console.log("请求拦截器")
  return config;
})

//响应拦截器
instance.interceptors.response.use(response => {
  console.log("响应拦截器")
  return response;
}), err => {
  console.log("响应出现错误时进入的拦截器")
}

//暴露instance实例对象
export default instance;
  • helloworld\src\views\Index.vue
<template>
  <div>
    <h1>欢迎进入员工管理系统首页 <span v-show="isLogin">欢迎: {{ admin.username }}</span></h1>
    <a href="javascript:;" @click="logout" v-show="isLogin">退出登录</a>
    <router-link v-show="!isLogin" :to="{name:'Login'}">立即登录</router-link>
    <hr/>
    <a href="#/index/emps">员工列表</a>
    <a href="#/index/depts">部门列表</a>
    <a href="#/index/orders">订单列表</a>
    <a href="#/index/users">用户列表</a>

    <router-view></router-view>
  </div>
</template>

<script>
import instance from "../utils/request";

export default {
  name: "Index",
  data() {
    return {
      admin: {}
    }
  },
  methods: {
    logout() {
      let token = localStorage.getItem("token");
      //根据token删除redis中用户登录标记
      instance.delete("/token?token=" + token).then(res => {
        // this.$router.push({path:'/'});//切换到主页
        this.admin = {};
      });
    }
  },
  computed: {
    isLogin() {
      return this.admin.username;//很多地方都用到了 admin.username,可以用 computed 计算属性 其实就是封装一下
    },
  },
  created() {
    //从localStorage获取token
    let token = localStorage.getItem("token");
    //发送axios 根据token获取用户信息
    instance.get("/token?token=" + token).then(res => {
      this.admin = res.data;
    });
  }
}
</script>

<style scoped>

</style>
  • helloworld\src\views\Login.vue
<template>
  <div>
    <form>
      用户名:<input type="text" v-model="admin.username"><br/>
      密码:<input type="text" v-model="admin.password"><br/>
      <input type="button" value="登录" @click="login">
    </form>
  </div>
</template>

<script>
import instance from "../utils/request";

export default {
  name: "Login",
  data() {
    return {
      admin: {}
    }
  },
  methods: {
    login() {
      //console.log(this.admin);
      //发送axios请求,登录成功
      //this.$router.push({name:'Index'});
      instance.post("/login", this.admin).then(res => {
        //console.log(res.data);
        const result = res.data;
        if (result.success) {
          //前端存储token信息  localStorage:本地缓存
          localStorage.setItem("token", result.token);
          //切换路由到主页
          this.$router.push({name: 'Index'});
        } else {
          alert(result.msg);
        }
      });
    }
  }
}
</script>

<style scoped>

</style>
  • helloworld\src\router\index.js
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
};

export default new Router({
  routes: [
 // {
 //   path: '/',
 //   name: 'HelloWorld',
 //   component: HelloWorld
 // }
    {
      path: '/',//默认路由,重定向到主页
      redirect: '/index'
    },
    {
      path: '/login',
      name: 'Login',
      component: () => import('../views/Login')//按需加载 用到的时候才用加载
    },
    {
      path: '/index',
      name: 'Index',
      component: () => import('../views/Index'),
      children: [
        {
          path: 'emps',
          name: 'Emps',
          component: () => import('../views/emp/Index')
        },
        {
          path: 'depts',
          name: 'Depts',
          component: () => import('../views/dept/Index')
        },
        {
          path: 'orders',
          name: 'Orders',
          component: () => import('../views/order/Index')
        },
        {
          path: 'users',
          name: 'Users',
          component: () => import('../views/user/Index')
        }
      ]
    },
    {
      path: '*',
      component: () => import('../views/404')
    }
  ]
})
  • helloworld\src\views\404.vue
<template>
  <div>
    <h2>404 Not Found 当前页面走丢了!!!点我立即回到
      <router-link :to="{name:'Index'}">主页</router-link>
    </h2>
  </div>
</template>

<script>
export default {
  name: "404"
}
</script>

<style scoped>

</style>
实现员工列表(增删改查)功能
  • com.baizhi.controller.EmpController
@RestController
@CrossOrigin
public class EmpController {


    @Resource
    private EmpService empService;


    @GetMapping("/emp")
    public Emp emp(Integer id) {
        return this.empService.queryById(id);
    }


    //查询所有
    @GetMapping("/emps")
    public List<Emp> emps() {
        return empService.queryAllByLimit(0, Integer.MAX_VALUE);
    }

    //添加员工信息
    @PostMapping("/emp")
    public void add(@RequestBody Emp emp) {
        if (StringUtils.isEmpty(emp.getId())) {
            empService.insert(emp);
        } else {
            empService.update(emp);
        }
    }

    //根据id删除员工信息
    @DeleteMapping("/emp/{id}")
    public void delete(@PathVariable("id") Integer id) {
        empService.deleteById(id);
    }

    //根据id查询一个员工信息
    @GetMapping("/emp/{id}")
    public Emp getById(@PathVariable("id") Integer id) {
        return this.empService.queryById(id);
    }
}
  • helloworld/src/views/emp/Index.vue
<template>
  <div>
    <h2>员工列表页面</h2>

    <button @click="addRow">添加员工信息</button>
    <table border="1" width="100%" style="text-align: center">
      <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>工资</th>
        <th>操作</th>
      </tr>
      <tr v-for="(emp,index) in emps" :key="emp.id">
        <td v-text="emp.id"></td>
        <td v-text="emp.name"></td>
        <td v-text="emp.age"></td>
        <td v-text="emp.salary"></td>
        <td><a href="javascript:;" @click="detailRow(emp.id)">修改</a> <a href="javascript:;" @click="delRow(emp.id)">删除</a></td>
      </tr>
    </table>
    <br/>
    <form>
      <div v-show="emp.id">
        编号:<input type="text" readonly v-model="emp.id"><br/>
      </div>
      姓名:<input type="text" v-model="emp.name"><br/>
      年龄:<input type="text" v-model="emp.age"><br/>
      工资:<input type="text" v-model="emp.salary"><br/>
      <input type="button" value="保存|修改" @click="saveOrUpdate(emp.id)">
    </form>
  </div>
</template>

<script>
import instance from "../../utils/request";

export default {
  name: "Index",
  data() {
    return {
      emps: [],
      emp: {}
    }
  },
  methods: {
    findAll() {
      instance.get("/emps").then(res => {
        console.log(res.data)
        this.emps = res.data
      })
    },
    addRow() {
      this.emp = {}
    },
    detailRow(id) {//定义根据id查询一个方法
      instance.get("/emp/" + id).then(res => {
        this.emp = res.data;
      })
    },
    saveOrUpdate() {
      if (!this.emp.name) {
        alert("请输入员工姓名");
        return false;
      }
      if (!this.emp.age) {
        alert("请输入员工年龄");
        return false;
      }
      if (!this.emp.salary) {
        alert("请输入员工工资");
        return false;
      }
      var _this = this;//因为箭头函数下的this作用域就不是之前的了,所以在箭头函数前把this赋给一个新的变量,这样的-this还是之前的this
      instance.post("/emp/", this.emp).then(res => {
        this.emp = {};
        this.findAll();
      }).catch(err => {
        console.log(err)
      })
    },
    delRow(id) {
      if (window.confirm("您确定要删除吗?")) {
        instance.delete("/emp/" + id).then(res => {
          this.findAll();
        })
      }
    }
  },
  created() {
    this.findAll();
  },
}
</script>

<style scoped>

</style>
vue cli 公共组件的提取
  • helloworld/src/views/Index.vue 中的<div>包裹的内容提取出来,然后在Index.vue中使用组件

在这里插入图片描述

代码仓库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

9.冄2.7.號

你的鼓励将是我创作的巨大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值