SpringBoot+Vue实现简单用户管理平台第一篇(后端接口设计)

Posted 全栈小袁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot+Vue实现简单用户管理平台第一篇(后端接口设计)相关的知识,希望对你有一定的参考价值。

本案例第二篇教程地址:SpringBoot+Vue实现简单用户管理平台第二篇(前端设计,接口对接)

demo地址预览(域名正在审核,将就ip访问):http://43.138.223.178/user-manager

花了几个小时做了一个SpringBoot+Vue的简单用户管理demo项目,适合新手教程,项目已在Gitee上开源,Gitee开源地址:https://gitee.com/yuandewei/Yuan-SpringBoot/tree/master

Gitee上开源的代码跟本次的案例的代码有些区别,本次案例稍微改了一点点,不过不影响Gitee上的项目运行,大致效果如下,功能可以访问demo地址测试哦

前言

开发环境

开发工具就不多介绍啦,就IDEA做后端,VSCode做前端,用其他的也都可以

技术

本次后端用到的技术呢: 主要就两个,SpringBoot + MyBatisPlus

前端的技术用到的技术: Vue,结合脚手架以及element ui框架开发前端

表设计

既然是用户管理嘛,肯定有用户表,我们先来设计表结构

这里说明一点,这次案例是新手教程,着重讲解功能的实现,所以用户信息参数方面就没有那么严格的校验,一般像号码这种字段肯定是设置为 char(11) 并且后端要校验的

创建Maven工程

创建一个空的Maven项目,大家应该都会了吧,还不会的小伙伴看之前的其他项目教程哦(我个人习惯创建maven工程,你喜欢直接创建springboot项目也可以,)

我这里创建好了一个 user-manager的maven项目,创建好项目,点击右下角选择自动导入,没有弹出来也没关系

引入依赖

	<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/>
    </parent>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- MyBatis-Plus依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- Web启动依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- SpringBoot测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 处理JSON的 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
    </dependencies>

基本配置

创建 com.xiaoyuan.usermanager 目录,新建一个启动类 UserManagerApplication

@SpringBootApplication
public class UserManagerApplication 
    public static void main(String[] args) 
        SpringApplication.run(UserManagerApplication.class, args);
    

在 resources 资源目录下新建 application.yml 配置文件

图片有误,少了时间格式转换三行代码,看下面

server:
  # 端口
  port: 8081

spring:
  # 数据源配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///l_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    username: xiaoyuan
    password: root
  # 时间格式转换
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
    
mybatis-plus:
  # mapper文件映射路径
  mapper-locations: classpath*:mapper/*.xml
  configuration:
    # 打印SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

db层

entity实体类

新建 db 包,在 db 包下新建 entitymapper 两个包,在 entity 包下新建一个 User

注意一下,图片里 describe 有个注解忘记加上了,以下面代码为准

@Data
public class User 

    // 主键ID
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    // 用户名
    private String username;

    // 昵称
    private String nickname;

    // 密码
    private String password;

    // 号码
    private String phone;

    // 性别
    private Character sex;

    // 描述
    @TableField(value = "`describe`") // describe属于数据库关键字,加上``区分
    private String describe;

    // 创建时间
    @TableField(fill = FieldFill.INSERT) // insert操作时自动注入时间
    private Date gmtCreate;

mapper数据访问层

mapper 包下新建一个 UserMapper,继承MyBatisPlus的 BaseMapper 类,作为DAO层操作数据

MyBaitsPlus配置

这里创建的两个包都与db包同级目录

config配置类

创建一个config包,新建一个MyBatisPlusConfig

@Configuration
public class MyBatisPlusConfig 

    /* 分页插件 */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() 
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 开启
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    

handler处理

新建一个 handler 包,包下新建一个 MyMetaObjectHandler 类,实现 MetaObjectHandler 类,改类有两个方法,一个insert…在向数据库插入数据的时候,会自动插入我们设置的值

@Component
public class MyMetaObjectHandler implements MetaObjectHandler 
    
    /**
     * 新增数据执行
     * 
     * insert插入数据到数据库操作时执行
     */
    @Override
    public void insertFill(MetaObject metaObject) 
        // 配置初始创建时间
        this.setFieldValByName("gmtCreate", new Date(), metaObject);
    

    /**
     * 修改数据执行
     * 
     * update修改数据库数据操作时执行
     */
    @Override
    public void updateFill(MetaObject metaObject) 
    

这里的 this.setFi… 第一个参数对应的是User实体类的名字,不是表中的字段名,第二个参数的默认值

Vo对象

新建一个 vo 包,用于和前端交互数据的类

统一结果返回类

vo 包下新建一个 R 类,作为我们统一返回给前端数据的类,

@Data
public class R 

    private Boolean success;

    private Integer code;

    private String message;

    private Map<String, Object> data = new HashMap<>();

    // 把构造方法私有化
    private R() 

    // 成功静态方法
    public static R ok() 
        R r = new R();
        r.setSuccess(true);
        r.setCode(200);
        r.setMessage("成功");
        return r;
    

    // 失败静态方法
    public static R error() 
        R r = new R();
        r.setSuccess(false);
        r.setCode(201);
        r.setMessage("失败");
        return r;
    

    public R success(Boolean success)
        this.setSuccess(success);
        return this;
    

    public R message(String message)
        this.setMessage(message);
        return this;
    

    public R code(Integer code)
        this.setCode(code);
        return this;
    

    public R data(String key, Object value)
        this.data.put(key, value);
        return this;
    

vo 包下新建 QueryParam

@Data
public class QueryParam 

    // 用户名
    private String username;

    // 昵称
    private String nickname;

    // 号码
    private String phone;

    // 性别
    private String sex;

    // 创建时间
    private String time;

这里讲一下吧,这个类是用来干嘛的呢?我们在效果展示的时候,是不是在上面看到有5个筛选条件,这5个筛选条件参数刚好对应类中的5个属性,我们统一封装起来

service业务层

新建一个 service 包,包下新建一个 UserService 接口类,继承MyBatisPlus的 IService

service 包下新建 impl 包,新建一个 UserServiceImpl 实现类,继承MyBatisPlus的 ServiceImpl 类,实现我们自己的 UserService

我们先在 UserService 接口类定义五个方法,接下来我们一一实现这五个功能

public interface UserService extends IService<User> 

    /**
     * 添加用户
     * @param user
     * @return
     */
    R insertUser(User user);

    /**
     * 删除单个用户
     * @param id 用户编号
     * @return
     */
    R deleteUser(Integer id);

    /**
     * 删除多个用户
     * @param ids 用户编号集合
     * @return
     */
    R deleteUserMore(List<Integer> ids);

    /**
     * 编辑用户
     * @param user
     * @return
     */
    R modifyUser(User user);

    /**
     * 分页查询用户列表
     * @param index 当前页
     * @param size 每页显示数量
     * @param queryParam 筛选条件对象
     * @return
     */
    R findUserList(Integer index, Integer size, QueryParam queryParam);

	/**
     * 查询单个用户详细信息
     * @param id 用户编号
     * @return
     */
    R getUserInfoById(Integer id);

添加用户

UserServiceImpl 实现类里实现添加用户方法,这里只做了简单的非空判断,其他参数的非法性校验可以自己额外完善

@Override
public R insertUser(User user) 
	if (user == null) return R.error().message("参数错误");

    // 用户名
	String username = user.getUsername();

	// 构建条件对象, 查询是否已经存在用户名
	QueryWrapper<User> wrapper = new QueryWrapper<>();
	wrapper.select("id");
	wrapper.eq("username", username);
	wrapper.last("limit 1");

	// 查询判断, 如果查询出来有数据, 则不为null
	if (this.baseMapper.selectOne(wrapper) != null) R.error().message("该用户名已存在");

	// 执行插入数据操作
	return this.baseMapper.insert(user) == 0 ? R.error().message("添加用户失败") : R.ok();

删除用户

删除用户就比较简单啦,肯定有人会问,前端做了非空校验,后端怎么还要做参数校验校验呢?

其实前后端都做是最好的,有绕过前端发送请求的,就比如我们自己测试接口时用的postman, apifox,后端多做一层校验,避免直接操作数据库,我这里也是比较简单的做了校验

@Override
public R deleteUser(Integer id) 
	if (id == null || id <= 0) return R.error().message("参数错误");

	return  this.baseMapper.deleteById(id) == 0 ? R.error().message("删除失败") : R.ok();

一键删除多个用户

删除多个用户也没难度,将多个用户的编号放到一个集合中,一次删除多个

@Override
public R deleteUserMore(List<Integer> ids) 
	if (ids.size() == 0) return R.error().message("参数错误");

	return this.baseMapper.deleteBatchIds(ids) != ids.size() ? R.error().message("删除失败") : R.ok();

编辑用户

这个也没什么难度,做个简单校验,然后根据ID更新用户信息(参数其他合法性校验可以自己额外做哦)

@Override
    public R modifyUser(User user) 
        if (user == null || user.getId() == null || user.getId() <= 0) return R.error().message("参数错误");
        
        return this.baseMapper.updateById(user) == 0 ? R.error().message("编辑用户失败") : R.ok();
    

获取单个用户信息

先实现这个吧,这个也很简单,直接通过用户编号查询用户的信息返回即可

@Override
public R getUserInfoById(Integer id) 
	if (id == null || id <= 0) return R.error().message("参数错误");
        
	return R.ok().data("userInfo", this.baseMapper.selectById(id));

查询用户列表

先创建编写SQL语句的文件,在 resources 下新建 mapper 包,包下新建 UserMapper.xml 文件
代码中的SQL语句不能包含注释,所以我在图片给出了每行的注释,代码中删掉了,对应看着

<?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">
<!-- namespace路径根据自己而定 -->
<mapper namespace="com.xiaoyuan.usermanager.db.mapper.UserMapper">

    <!-- 查询用户列表(带多条件) -->
    <select id="findUserList" resultType="com.xiaoyuan.usermanager.db.entity.User">
        select *
        from user
        <where>
            1 = 1
            <if test="queryParam != null">
                <if test="queryParam.username != null and queryParam.username != ''">
                    and username = #queryParam.username
                </if>
                <if test="queryParam.nickname != null and queryParam.nickname != ''">
                    and nickname like CONCAT(#queryParam.nickname, '%')
                </if>
                <if test="queryParam.phone != null and queryParam.phone != ''">
                    and phone = #queryParam.phone
                </if>
                <if test="queryParam.sex != null and queryParam.sex != ''">
                    and sex = #queryParam.sex
                </if>
                <if test="queryParam.time != null and queryParam.time != ''">
                    and DATE_FORMAT(gmt_create,'%Y-%m-%d') = #queryParam.time
                </if>
            </if>
        </where>
    </select>
</mapper>

我们大致来分析一下上面的SQL语句,首先 select * from user这里没毛病吧,咋们是管理用户,所有信息都得要上,* 查询所有

后面 where 里有个 1 = 1 作用是恒等式,为了防止没有做筛选条件时,queryParam 条件对象为 null 导致 where 里面没东西,执行SQL语句的时候就会出现 where 后面没加东西,就会抛出异常

CONCAT 函数的作用是拼接,当然你直接用下面这样也行,推荐还是使用CONCAT,以后会遇到的,还有就是 % 只写右边就可以了,避免全表扫描,采用单模糊查询

and nickname like #queryParam.nickname '%'

DATE_FORMAT 函数作用是对时间进行格式化,2022-05-06 11:17:36 转换为 2022-05-06

SQL语句写好了,更新一下我们的 UserMapper 类,映射到 UserMapper.xml 文件的SQL语句,函数名就是 < select id=“xxxx”> 这里id的值

@Repository
public 

SpringBoot+Vue实现简单用户管理平台第二篇(前端设计,接口对接)

本案例第一篇后端教学地址:https://blog.csdn.net/weixin_47971206/article/details/124741442?spm=1001.2014.3001.5501

demo地址预览:http://xiaoyuan-boke.com/user-manager

🚀 花了几个小时做了一个SpringBoot+Vue的简单用户管理demo项目,适合新手教程,项目已在Gitee上开源,Gitee开源地址:https://gitee.com/yuandewei/Yuan-SpringBoot/tree/master

Gitee上开源的代码跟本次的案例的代码有些区别,本次案例稍微改了一点点,不过不影响Gitee上的项目运行,大致效果如下,功能可以访问demo地址测试哦


话不多说,下面跟着我一步一步来吧~

前言

说实话,我个人觉得像这种的简单的crud项目前端比后端要麻烦多,也做的比后端慢

开发前准备

  • VSCode开发工具,很方便,推荐使用,你用其他前端编译器也没有问题(我之前出过安装教程)
  • Vue环境的搭建,一个 node.js(我之前也出过安装教程,可以翻翻),一个 vue-cli 脚手架(网上很多教程),只要 node.js 成功安装配置没问题,脚手架安装很快的
  • 打开我们的 element ui 网站,地址:https://element.eleme.cn/#/zh-CN/component/installation
  • 打开 axios 官方网站,地址:http://www.axios-js.com/docs/

🍒 脚手架创建项目,安装依赖

提一嘴,VSCode记得要安装Vue环境的插件,这个百度也有很多教程,就不介绍啦,还有就是要有一定Vue基础知识,至少了解vue的语法

创建项目

首先以管理员模式打开我们的 VSCode 工具,选择文件夹位置(待会创建项目的地方)

在下面终端输命令 vue create user-manager-vue 后面那个是项目名称,下面没有终端控制台的可以点击上面新建终端

选择 default 默认配置就好,我这里创建过做过配置,有自己单独的配置选项,你们第一次创建的选择默认的就行,然后等待创建完成

如图,一个脚手架的项目就创建好啦

我们什么都不做,先运行看看默认项目的样子吧,输入命令 cd user-manager-vue 跳转到项目目录下,接着输入 npm run serve 启动项目

显示如图,则启动成功,打开网站看一下

效果如图,这是脚手架创建项目的默认模板

安装依赖

回到编译器,ctrl c 结束运行,输入命令 npm i element-ui -S 安装element ui,接着输入命令 npm install axios 安装axios,用于向后端发送请求

🍅 初始配置,封装axios请求

修改main.js,加上下面五行代码,全局使用

import ElementUI from 'element-ui';
import axios from 'axios'
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);
Vue.prototype.$axios = axios

封装请求

src 目录下新建 utils 文件夹,文件夹下新建 request.js 文件,代码内容我已经写有注释

import axios from 'axios'
import  Message  from 'element-ui'

// 创建axios实例
const service = axios.create(
  baseURL: "http://localhost:8081", // 后端接口地址前缀(端口和后端配置文件一致)
  timeout: 5000 // 请求超时时间
)

// request拦截器
service.interceptors.request.use(
  config => 
    // 请求头携带我们的权限码,和后端一致就行
    config.headers['Authentication'] = "dwagfhwhgiawpfwabifpjwaidjwaidwiafihwigfhwaigwhaipgwaihiwahifhwdefef"
    return config
  ,
  error => 
    // Do something with request error
    console.log(error) // for debug
    Promise.reject(error)
  
)

// response 拦截器
service.interceptors.response.use(
  response => 
    /**
     * code为非200是抛错 可结合自己业务进行修改
     */
    const res = response.data
    if (res.code !== 200) 
      Message(
        message: res.message,
        type: 'error',
        duration: 5 * 1000
      )
      /**
       * 可以自定义返回状态码,处理不同的结果
       */
      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

修改app.vue

修改成下面这样

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<style>
.el-header 
  background-color: #b3c0d1;
  color: #333;
  line-height: 60px;


.el-aside 
  color: #333;

</style>

修改views文件夹

删除原来的 Home.vueAbout.vue ,新建 index.vue,作为左侧导航栏组件(看demo地址效果)

再分别新建三个页面 userList.vueaddUseruserInfoList.vue,三个页面分别对应左侧导航栏展开的三个页面

接着在 index.vue 添加代码,

<template>
  <div class="app">
    <el-container style="height: 700px; border: 1px solid #eee">
      <!-- 左侧导航栏 -->
        <el-aside width="200px"
                  style="background-color: rgb(238, 241, 246)">
            <el-menu router="router" :default-openeds="['0']">
                <el-submenu v-for="(item,index) in $router.options.routes"
                            :key="index"
                            :index="String(index)">
                    <template slot="title"><i :class="item.class"></i>item.name</template>
                    <el-menu-item v-for="(item1,index1) in item.children"
                                  :key="String(index1)"
                                  :index="item1.path"
                                  v-if="!(item1.hidden)"
                                  :class="$router.path==item1.path?'is-active':''">item1.name</el-menu-item>
                </el-submenu>
            </el-menu>
        </el-aside>

        <!-- 顶部右侧工具栏 -->
        <el-container>
            <el-header style="text-align: right; font-size: 12px">
                <el-dropdown>
                    <i class="el-icon-setting"
                       style="margin-right: 15px"></i>
                    <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item>功能一</el-dropdown-item>
                        <el-dropdown-item>功能二</el-dropdown-item>
                        <el-dropdown-item>功能三</el-dropdown-item>
                    </el-dropdown-menu>
                </el-dropdown>
                <span>管理员</span>
            </el-header>

            <!-- 中间区域内容页面的切换 -->
            <el-main>
                <router-view />
            </el-main>
        </el-container>
    </el-container>
  </div>
</template>

<script>

export default 

</script>

样式参考 element ui 的布局组件,下面有详细的属性说明哦

接着在另外三个页面先随便写点东西,输入<vue 会自动弹出模板



修改router路由

修改 router 文件夹下的 index.js,主要改两个地方,我直接全部代码粘贴出来

import Vue from 'vue'
import VueRouter from 'vue-router'
import index from '../views/index.vue'

Vue.use(VueRouter)

const routes = [
  
		path: "/",
		name: "用户管理",
		component: index,
    class: 'el-icon-s-custom',
		children: [
			
				path: "/",
				name: "数据列表",
				component: () => import("../views/userList.vue")
			,
      
				path: "/addUser",
				name: "添加用户",
				component: () => import("../views/addUser.vue")
			,
      
				path: "/editUser/:id",
				name: "编辑用户",
				component: () => import("../views/addUser.vue"),
        hidden: true //隐式路由
			,
			
				path: "/userInfo",
				name: "用户信息",
				component: () => import("../views/userInfoList.vue")
			
		],
	,
]

const router = new VueRouter(
  mode: 'history',
  base: process.env.BASE_URL,
  routes
)

export default router

简要说明一下,children列表里面的是二级路由,外面的是一级路由

初始操作做完了,先运行看看效果 npm run serve,看到图片的效果说明已经OK了

封装API接口

src文件夹下新建 api 文件夹,文件夹下新建 user.js,封装和后端对接的接口请求

import request from '../utils/request'

export default 

  // 添加用户接口
  addUser(user) 
    return request(
      url: '/user',
      method: 'post',
      data: user
    )
  ,

  // 删除用户接口
  deleteUser(id) 
    return request(
      url: `/user/$id`,
      method: 'delete'
    )
  ,

  // 删除多个用户接口
  deleteMoreUser(ids) 
    return request(
      url: '/user',
      method: 'delete',
      data: ids
    )
  ,

  // 编辑用户接口
  updateUser(user) 
    return request(
      url: `/user`,
      method: 'put',
      data: user
    )
  ,

  // 查询用户列表接口
  findUserList(index,size, queryParam) 
    return request(
      url: `/user/$index/$size`,
      method: 'post',
      data: queryParam
    )
  ,

  // 根据用户编号获取用户信息接口
  getUserInfo(id) 
    return request(
      url: `/user/$id`,
      method: 'get'
    )
  

🌲 用户信息页面

我们先来实现这个页面的功能,相对简单一点,就一个查询的操作,页面模板在element ui这个位置

我们复制下面给出来的源码,粘贴到 userInfoList.vue 页面的 < div class=app>粘贴到这里< /div>

稍做修改,改成我们需要的格式

<div class="app">
      <el-descriptions class="margin-top"
                       title="小袁同学"
                       :column="3"
                       size="medium"
                       border>

        <!-- 用户名 -->
        <el-descriptions-item>
          <template slot="label">
            <i class="el-icon-user"></i>
            用户名
          </template>
          xiaoyuan
        </el-descriptions-item>

        <!-- 手机号码 -->
        <el-descriptions-item>
          <template slot="label">
            <i class="el-icon-mobile-phone"></i>
            手机号
          </template>
          12345678910
        </el-descriptions-item>

        <!-- 性别 -->
        <el-descriptions-item>
          <template slot="label">
            <i class="el-icon-s-check"></i>
            性别
          </template></el-descriptions-item>

        <!-- 注册时间 -->
        <el-descriptions-item>
          <template slot="label">
            <i class="el-icon-tickets"></i>
            注册时间
          </template>
          2022-05-06 23:56:37
        </el-descriptions-item>

        <!-- 个人描述 -->
        <el-descriptions-item>
          <template slot="label">
            <i class="el-icon-edit-outline"></i>
            个人描述
          </template>
         我性格开朗,希望和你交个朋友哦
        </el-descriptions-item>
      </el-descriptions>
    </div>

created 周期函数给后端发送请求,我们打印一下看看数据格式

🔥 注意!记得启动后端的项目,同时controller控制层加上跨域注解,上一篇教程忘记加了

<script>
// 引入接口
import user from '../api/user'

export default 
  data() 
    return 
      // 用户信息列表
      userList: [],
    
  ,
  created() 
    // 发送请求,获取用户信息列表
    user.findUserList(1, 10).then((res) => 
      // 赋值
      this.userList = res.data.userList
      console.log(this.userList)
    )
  ,

</script>

保存代码打开浏览器刷新,成功看到数据

最后加上分页组件,通过 v-for 标签遍历用户列表显示到页面上,代码有注释,完整代码如下

<template>
  <div class="app">
    <div :class="index > 0 ? 'info' : ''" v-for="(item, index) in userList" :key="index">
      <el-descriptions class="margin-top"
                       :title="item.nickname"
                       :column="3"
                       size="medium"
                       border>

        <!-- 用户名 -->
        <el-descriptions-item以上是关于SpringBoot+Vue实现简单用户管理平台第一篇(后端接口设计)的主要内容,如果未能解决你的问题,请参考以下文章

基于Java springboot+vue+redis前后端分离家具商城平台系统设计和实现

基于JAVA springboot+VUE前后端分离疫情防疫平台设计实现

基于springboot+vue+redis前后端分离家具商城平台系统项目(源码+论文初稿可以直接运行)015主要设计:用户登录注册商城分类商品浏览查看购物车订单支付以及后台的管理

SpringBoot+Vue+Redis前后端分离家具商城平台系统(源码+论文初稿直接运行《精品毕设》)15主要设计:用户登录注册商城分类商品浏览查看购物车订单支付以及后台的管理

计算机毕业设计springboot+vue+elementUI高速公路收费管理系统设计与实现

基于Java+SpringBoot+vue+element实现火车订票平台管理系统

(c)2006-2024 SYSTEM All Rights Reserved IT常识