Springboot+Vue前后端分离项目-管理系统-下

承接Springboot+Vue前后端分离项目-管理系统-上

在上篇我们完成了登录页的前端布局以及后端接口

我们在这里完成管理页的全部任务


一.用户管理页面

用户管理布局

搜索栏

我们使用element标签来创建页面 组件 | Element

使用什么样式就直接在里面找就可以了,不需要死记

<template>
  <div>
    <!-- 搜索栏 -->
 
        <el-input v-model="input" placeholder="用户名"></el-input>
        <el-input v-model="input" placeholder="电话"></el-input>
        <el-button type="primary" round>查询</el-button>
 
  </div>
</template>
 
<script>
export default {
 
}
</script>
 
<style>
 
</style>

 为了美观,我们使用card样式将其套起来

但是此时卡片的边界与左栏上栏默认的贴在了一起,依旧不美观,我们可以直接修改一下卡片外边距,但是并不推荐这种做法,因为之后还有别的组件需要修改边距,我们这里修改整个区域的css 

整个区域指的这里

 我们打开F12查看此区域叫什么名字

 我们在根组件App.vue中设置style,定义一个全局边距

<style>
.app-main{
  /* 边距 */
  padding: 10px;
}
</style>

 

再来修改一下框的宽度、右边距以及查询按钮的图案,这里仅修改这个地方的框大小就行了,没必要设置全局属性 

<template>
  <div>
    <!-- 搜索栏 -->
    <el-card id="search">
        <el-input v-model="input" placeholder="用户名"></el-input>
        <el-input v-model="input" placeholder="电话"></el-input>
        <el-button type="primary" round icon="el-icon-search">查询</el-button>
    </el-card>
  </div>
</template>
 
<script>
export default {
 
}
</script>
 
<style>
#search .el-input{
    width: 200px;
    margin-right: 10px;
}
</style>

十分甚至九分的美观啊(喜

若是想在这卡片末尾添加一个小按钮,我们可以使用Layout布局页面

这部分的完整代码如下

<template>
  <div>
    <!-- 搜索栏 -->
    <el-card id="search">
        <el-row>
            <el-col :span="20">
                <el-input v-model="input" placeholder="用户名"></el-input>
                <el-input v-model="input" placeholder="电话"></el-input>
                <!-- icon是图标,可以修改为自己的 -->
                <el-button type="primary" round icon="el-icon-search">查询</el-button>
            </el-col>
            <!-- align是对齐,right就是右对齐 -->
            <el-col :span="4" align="right">
                <el-button type="primary" icon="el-icon-plus" circle></el-button>
            </el-col>
        </el-row>
 
    </el-card>
  </div>
</template>
 
<script>
export default {
 
}
</script>
 
<style>
#search .el-input{
    width: 200px;
    margin-right: 10px;
}
</style>

 结果栏

 我们同样将结果列表放入card组件,就是为了美观,不放也行

可以依照上面那样在根组件App.vue中设置全局底边距,这样别的该种类组件也能用,如果只是想在本组件生效那就写在本类文件的style中

先查看该组件css名称

 

.el-card{
  /* 底部边距 */
  margin-bottom: 10px ;
}

  我们来使用表格替换掉xxx

  直接使用的话会报错,因为数据绑定不正常

 我们先在script中创建空数据,因为data中返回的searchModel是对象类型,所以调用的时候要使用“对象.属性”的形式,而userList是数组,直接使用即可

<script>
export default {
    data(){
        return{
            searchModel: {},
            userList: []
        }
    }
};
</script>

修改我们之前的代码绑定的数据  

添加/修改我们需要的字段,对应数据库字段即可

 添加分页组件,在element中查找分页组件组件 | Element 

    <el-pagination
        方法1
      @size-change="handleSizeChange"
        方法2
      @current-change="handleCurrentChange"
        参数1:当前页
      :current-page="currentPage4"
        参数2:展示数
      :page-sizes="[100, 200, 300, 400]"
        参数3:页数,不要写死,用变量代替
      :page-size="100"
      layout="total, sizes, prev, pager, next, jumper"        
        参数4:总记录数:这个是由后端传来的数据,不能写常量
      :total="400">
    </el-pagination>

这里面绑定了两个方法4个参数,所以不能直接使用,要先在script中定义出来,不然会报错 

<script>
export default {
    data(){
        return{
            total:0,
            searchModel: {
                pageNo:1,
                pageSize:10
            },
            userList: []
        }
    },
    methods:{
        handleSizeChange(){
 
        },
        handleCurrentChange(){
 
        }
    }
};
</script>

更改一下分页展示的语言

 最终效果

这部分的全部代码 

<template>
  <div>
    <!-- 搜索栏 -->
    <el-card id="search">
      <el-row>
        <el-col :span="20">
          <el-input v-model="searchModel.username" placeholder="用户名"></el-input>
          <el-input v-model="searchModel.phone" placeholder="电话"></el-input>
          <!-- icon是图标,可以修改为自己的 -->
          <el-button type="primary" round icon="el-icon-search">查询</el-button>
        </el-col>
        <!-- align是对齐,right就是右对齐 -->
        <el-col :span="4" align="right">
          <el-button type="primary" icon="el-icon-plus" circle></el-button>
        </el-col>
      </el-row>
    </el-card>
 
    <!-- 结果栏 -->
    <el-card>
 
      <el-table :data="userList" stripe style="width: 100%">
        <el-table-column type="index" label="#" width="180">
        </el-table-column>
        <el-table-column prop="id" label="ID" width="180">
        </el-table-column>
        <el-table-column prop="username" label="用户名" width="180">
        </el-table-column>
        <el-table-column prop="phone" label="电话" width="180"> 
        </el-table-column>
        <el-table-column prop="mail" label="电子邮箱"> 
        </el-table-column>
        <el-table-column label="操作" width="180"> 
        </el-table-column>
 
      </el-table>
 
      <!-- 分页组件 -->
 
    <el-pagination
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="searchModel.pageNo"
      :page-sizes="[5, 10, 15, 20]"
      :page-size="searchModel.pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total">
    </el-pagination>
 
    </el-card>
  </div>
</template>
 
<script>
export default {
    data(){
        return{
            total:0,
            searchModel: {
                pageNo:1,
                pageSize:10
            },
            userList: []
        }
    },
    methods:{
        handleSizeChange(){
 
        },
        handleCurrentChange(){
 
        }
    }
};
</script>
 
<style>
#search .el-input {
  width: 200px;
  margin-right: 10px;
}
</style>

2.用户管理接口 

我们首先要实现分页查询,具体可以查看mybatis的官网插件主体 | MyBatis-Plus (baomidou.com)

创建分页插件拦截器

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

Controller层

    @GetMapping("/list")
    //                                                required表示查询时Params不必带上该参数
    public Result<Map<String,Object>> getUserList(@RequestParam(value = "username",required = false) String username,
                                              @RequestParam(value = "phone",required = false) String phone,
                                              @RequestParam("pageNo") Long pageNo,
                                              @RequestParam("pageSize") Long pageSize){
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //判断长度是否大于零,其实判断你username是不是非空
        wrapper.eq(StringUtils.hasLength(username),User::getUsername,username);
        wrapper.eq(StringUtils.hasLength(phone),User::getPhone,phone);
        Page<User> page = new Page<>(pageNo, pageSize);
 
        userService.page(page,wrapper);
 
        Map<String,Object> data = new HashMap<>();
        data.put("total",page.getTotal());
        data.put("rows",page.getRecords());
 
        return Result.success(data);
    }

测试

3.接口对接

我们若是想要对接,首先在vue中需要定义api,前面做登录接口时,模板已经帮我们定义好了登录接口部分,现在我们需要自己来实现

一个模块一个js文件,我们在api文件夹下创建userManage.js

import request from '@/utils/request'
 
export default {
    // searchModel是前端传来的数据
    getUserList(searchModel){
        return request({
            url: '/user/list',
            method: 'get',
            // post上传的是json格式,get上传的params
            params:{
                pageNo: searchModel.pageNo,
                pageSize: searchModel.pageSize,
                username: searchModel.username,
                phone: searchModel.phone
            }
        });
    },
    // 有别的方法在这里写
}

在user.vue中引入 

我们为查询按钮绑定一下事件 

 

查看效果 

此时我们看到并没有1页展示5条数据,是因为没有定义这两个方法,当变量发生改变时调用方法

        handleSizeChange(pageSize){
            this.searchModel.pageSize = pageSize;
            this.getUserList();
        },
        handleCurrentChange(pageNo){
            this.searchModel.pageNo = pageNo;
            this.getUserList();
        },

成功实现 

4.密码加密 

我们使用MD5算法进行密码加密,md5有不可逆转的特点

1.引入依赖

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
		</dependency>

2.在启动类中配置

	@Bean
	public PasswordEncoder passwordEncoder(){
		return new BCryptPasswordEncoder();
	}

3.设计新增用户的接口 

@RestController
@RequestMapping("/user")
public class UserController { 
   @Autowired
    private PasswordEncoder passwordEncoder;

        @PostMapping
    //前端传来的user对象是一个json格式
    public Result<?> addUser(@RequestBody User user){
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        //这个方法已经进行了加盐处理,相同的初始数据生成的值不一样
        userService.save(user);
        return Result.success("新增用户成功");
    }
}

 4.设计登录用户的接口

我们先根据用户名查询判断,再来进行密码匹配

我们在Service层重新设计登录逻辑

    @Override
    public Map<String, Object> login(User user) {
        //根据用户名与密码查询结果
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername,user.getUsername());
//        wrapper.eq(User::getPassword,user.getPassword()); 引入了MD5加密,就不能直接比对密码了
        User loginUser = this.baseMapper.selectOne(wrapper);

        //若结果不为空,并且密码和传入密码匹配则生成token,并将用户信息存入redis
        if (loginUser!=null && passwordEncoder.matches(user.getPassword(),loginUser.getPassword())){
            //暂时用UUID,终极方案应为jwt
            String key = "user:" + UUID.randomUUID();

            //存入redis
            loginUser.setPassword(null);    //密码不要存进去
            //默认这是永久有效的,要设置timeout时间
            redisTemplate.opsForValue().set(key,loginUser,30, TimeUnit.MINUTES);

            //返回数据
            Map<String,Object> data = new HashMap<>();
            data.put("token",key);
            return data;
        }
        return null;
    }

 查看效果(记得把数据库中的admin的密码改成刚才生成的md5值形式)

 可以成功登录

5.用户修改

后端端口

    @PutMapping
    //前端传来的user对象是一个json格式
    public Result<?> updateUser(@RequestBody User user){
        user.setPassword(null);
        userService.updateById(user);
        return Result.success("修改用户成功");
    }
    
    //根据id查询用户信息,方便修改的时候在框中展现信息以方便对照修改
    @GetMapping("/{id}")
    public Result<User> getUserById(@PathVariable("id") Integer id){
        User user = userService.getById(id);
        return Result.success(user);
    }

 前端接口

我们先用插槽新增两个按钮

        <el-table-column label="操作" width="180">
          <template slot-scope="scope">
            <el-button @click="openEditUI(scope.row.id)" type="primary" icon="el-icon-edit" circle size="mini"></el-button>
            <el-button type="danger" icon="el-icon-delete" circle size="mini"></el-button>
          </template>
        </el-table-column>

 

api接口

    addUser(user){
        return request({
            url: '/user',
            method: 'post',
            data: user
        });
    },
    updatedUser(user){
        return request({
            url: '/user',
            method: 'put',
            data: user
        })
    },
    //这个api是为了判断是新增还是修改
    saveUser (user){
        if(user.id == null && user.id == undefined){
            //新增
            return this.addUser(user);
        }
        //修改
        return this.updatedUser(user);
    },
    getUserById(id){
        return request({
            url:`/user/${id}`,
            method: 'get'
        })
    }

这里我们为了优化代码,也为了减少代码的繁琐我们的新增窗口与修改窗口使用的是同一个,但是我们要设置条件来区分展示

我们让密码栏不要展示出来,逻辑上密码不应该在这里修改,当新增时就展示,修改就隐藏

        <el-form-item v-if="userForm.id == null || userForm.id == undefined"
         label="登录密码" 
         prop="password" 
         :label-width="formLabelWidth"
         >

 我们在js代码中的方法中也设置条件,当id是null的时候是新增,当id不为null的时候说明当前有对象存在,是修改

  methods: {
    openEditUI(id){
      if(id == null){
      this.title = '新增用户';
      this.dialogFormVisible = true;
      }else{
        this.title = '修改用户';
        //根据用户查询id
        userApi.getUserById(id).then(response =>{
          this.userForm = response.data;
        });
      }
      this.dialogFormVisible = true;
    },
  },

查看效果

 

 

5.用户删除

我们不进行物理删除,我们使用逻辑删除,就是数据库表字段中有一个delete字段,删除时是把0改为1

配置全局配置

在application.yml中

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted    #全局逻辑删除的实体字段名
      logic-delete-value: 1          #逻辑已删除 默认为1
      logic-not-delete-value: 0      #逻辑未删除 默认为0

前端

    deleteUserById(id){
        return request({
            url: `/user/${id}`,
            method: 'delete'
        })
    }
deleteUser(user){
      this.$confirm(`您确认删除用户${user.username}吗?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          userApi.deleteUserById(user.id).then(response => {
            this.$message({
              type: 'success',
              message: response.message
            });
            this.getUserList();
          });
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });          
        });
    },

我们删除用户a

 发现delete操作数据库是update操作

二、角色管理

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
springbootvue是现代web开发中最常用的后端和前端框架之一。前后端分离是一种架构模式,将前端和后端的开发分离,通过接口来进行数据传输和交互。 在线教育系统作为一个实战项目,可以基于springbootvue来开发。首先,我们可以使用springboot来构建后端的RESTful API,处理用户请求、数据持久化等功能。通过使用springboot的优秀特性,可以快速搭建项目的基础框架,并集成常用的开发组件,如Spring MVC、Spring Data JPA等。 而在前端方面,可以使用vue来开发用户界面。vue是一个轻量级的JavaScript框架,可以帮助我们构建交互式的用户界面。通过vue的组件化开发和数据绑定机制,可以提高前端开发效率,并实现良好的用户体验。 在实现前后端分离的时候,可以通过定义RESTful API接口来进行数据的传输和交互。后端使用springboot提供的@RestController注解来处理HTTP请求,并返回JSON格式的数据。前端通过vue的axios库来进行异步请求和交互,获取后端提供的数据并展示在页面上。 在线教育系统常见的功能包括用户登录、课程管理、视频播放等。用户登录可以通过JWT来实现身份认证和授权。课程管理可以包括课程的发布、编辑、删除等功能,通过操作数据库来实现数据的增删改查。视频播放可以通过集成第三方视频播放器,如video.js来实现。 总结来说,使用springbootvue进行前后端分离的在线教育系统的实战项目,可以通过定义RESTful API接口、使用axios进行数据交互和展示、集成第三方插件实现各种功能。通过这种方式,可以提高项目的开发效率、降低耦合性,并实现良好的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

An1ong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值