1.需求分析

一个音乐管理系统包括:

1.用户信息管理:该模块主要由管理员进行操作,将所有用户的用户名、密码、邮箱、创建时间以及用户状态列在一张表,管理员可以进行增加、删除(批量删除)、修改以及查询用户信息。

2.歌手信息管理:该模块主要包括歌手的姓名、昵称、性别、歌手照片、歌手出生日期、居住地址、歌手简介。具有增加、删除(逻辑删除、批量删除)、修改以及查询(分页查询、模糊查询)歌手信息等功能。

3:歌曲信息管理:该模块主要包括歌曲名、歌曲简介、发行封面、发行时间、歌词以及更新时间等。有增加、删除(逻辑删除、批量删除)、修改以及查询(分页查询、模糊查询)歌手信息等功能。

4.评论管理:该模块主要包括评论内容、评论类型、是否置顶推荐、评论时间,删除评论等功能。

(还有日志管理,权限管理等功能后续开发)

2.数据库开发

根据上述需求分析进行数据库设计

1.用户信息表   user

用户id id INT
用户名 name VARCHAR(45)
密码 password VARCHAR(45)
邮箱 email VARCHAR(45)
手机号 phone CHAR(15)
出生日期 birth DATETIME
头像 avator VARCHAR(255)
创建时间 create_time DATETIME
更新时间 update_time DATETIME

2.歌手信息表  singer

歌手id id INT
歌手名 name VARCHAR(45)
歌手昵称 nickName VARCHAR(45)
歌手性别 sex TINYINT(4)
歌手照片 pic VARCHAR(255)
出生日期 birth DATETIME
居住地址 address VARCHAR(45)
歌手介绍 introduction VARCHAR(255)
是否删除 is_delete VARCHAR(20)

3.歌曲信息表  song

歌曲id id INT
歌手id singer_id INT(10)
歌曲名 name VARCHAR(45)
歌曲简介 introduction VARCHAR(255)
发行时间 create_time DATETIME
更新时间 update_time DATETIME
发行照片 pic VARCHAR(255)
歌词 lyric TEXT
歌曲地址 url VARCHAR(255)
是否删除 is_delete VARCHAR(20)

4.歌单表  song_list

歌单id id INT
歌单标题 title VARCHAR(255)
歌单图片 pic VARCHAR(255)
歌单简介 introduction TEXT
歌单风格 style VARCHAR(10)

5. 歌曲歌单关联表

歌单id id INT(10)
歌曲id song_id INT(10)
歌单id song_list_id INT(10)

6. 歌曲分类表  song_type

类型id id INT(10)
歌曲id song_id INT(10)
评论id comment_id INT(10)
歌曲类型 type VARCHAR(255)

7.评论表  comment

评论id id INT
用户id user_id INT(10)
歌曲id song_id VARCHAR(45)
歌单id song_list_id INT(10)
评论内容 content VARCHAR(255)
创建时间 create_time DATETIME
是否置顶 is_top INT(10)

具体的sql如下代码所示:没有插入数据,自己随便插入一些就可以

CREATE DATABASE /*!32312 IF NOT EXISTS*/`t_music` /*!40100 DEFAULT CHARACTER SET utf8 */;USE `t_music`;DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`name` VARCHAR(45) NOT NULL COMMENT '用户名',`password` VARCHAR(45) NOT NULL COMMENT '密码',`email` VARCHAR(45) COMMENT '邮箱',`phone` CHAR(15) DEFAULT NULL COMMENT '手机号',`birth` DATETIME DEFAULT NULL COMMENT '出生日期',`avator` VARCHAR(255) DEFAULT NULL COMMENT '头像',`create_time` DATETIME NOT NULL COMMENT '创建时间',`update_time` DATETIME NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT '用户信息表';DROP TABLE IF EXISTS `singer`;
CREATE TABLE `singer` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`name` VARCHAR(45) NOT NULL COMMENT '歌手名',`nickName` VARCHAR(45)  COMMENT '歌手昵称',`sex` TINYINT(4) DEFAULT NULL COMMENT '歌手性别',`pic` VARCHAR(255) DEFAULT NULL COMMENT '歌手照片',`birth` DATETIME DEFAULT NULL COMMENT '出生日期',`address` VARCHAR(45) DEFAULT NULL COMMENT '居住地址',`introduction` VARCHAR(255) DEFAULT NULL COMMENT '歌手介绍',`is_delete` VARCHAR(20) NOT NULL DEFAULT '0' COMMENT '是否逻辑删除 0-否,1-是',PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8 COMMENT '歌手信息表';DROP TABLE IF EXISTS `song`;
CREATE TABLE `song` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`singer_id` INT(10) UNSIGNED NOT NULL,`name` VARCHAR(45) NOT NULL COMMENT '歌曲名',`introduction` VARCHAR(255) DEFAULT NULL COMMENT '歌曲简介',`create_time` DATETIME NOT NULL COMMENT '发行时间',`update_time` DATETIME NOT NULL COMMENT '更新时间',`pic` VARCHAR(255) DEFAULT NULL COMMENT '发行照片',`lyric` TEXT COMMENT '歌词',`url` VARCHAR(255) NOT NULL COMMENT '歌曲地址',`is_delete` VARCHAR(20) NOT NULL DEFAULT '0' COMMENT '是否逻辑删除 0-否,1-是',PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8 COMMENT '歌曲信息表';DROP TABLE IF EXISTS `song_list`;
CREATE TABLE `song_list` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`title` VARCHAR(255) NOT NULL COMMENT '歌单标题',`pic` VARCHAR(255) DEFAULT NULL COMMENT '歌单图片',`introduction` TEXT COMMENT '歌单简介',`style` VARCHAR(10) DEFAULT '无' COMMENT '歌单风格',PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=42340354 DEFAULT CHARSET=utf8 COMMENT '歌单表';DROP TABLE IF EXISTS `comment`;
CREATE TABLE `comment` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`user_id` INT(10) UNSIGNED NOT NULL COMMENT '用户id',`song_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '歌曲id',`song_list_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '歌单id',`content` VARCHAR(255) DEFAULT NULL COMMENT '评论内容',`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',`is_top` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否置顶  0-未置顶,1-置顶',PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS `song_list_linked`;
CREATE TABLE `song_list_linked` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`song_id` INT(10) UNSIGNED NOT NULL,`song_list_id` INT(10) UNSIGNED NOT NULL,PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=212 DEFAULT CHARSET=utf8;DROP TABLE IF EXISTS `song_type`;
CREATE TABLE `song_type` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`song_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '歌曲id',`comment_id` INT(10) UNSIGNED DEFAULT NULL COMMENT '评论id',`type` VARCHAR(255) DEFAULT NULL COMMENT '歌曲类型',PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=64 DEFAULT CHARSET=utf8 COMMENT '歌曲类型表';

3. 歌手信息后台开发

通过代码生成器,整合swagger进行后台相关开发,主要目录如下

对应的pom文件:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>music</artifactId><version>0.0.1-SNAPSHOT</version><name>music</name><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.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.31</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.2.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.7.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

application.properties:

# 服务端口
server.port=8888# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/t_music?serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8#mybatis日志
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis-plus.mapper-locations=classpath:com/example/serviceedu/mapper/xml/*.xmlmybatis-plus.mapper-locations=classpath*:mapper/*.xml,classpath:mapper/**/*Mapper.xml

运行codegenerator代码生成器,生成相应的代码,比如生成singer相关的代码

CodeGenerator代码生成器代码如下:

package com.example.music.config;import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;public class CodeGenerator {/*** <p>* 读取控制台内容* </p>*/public static String scanner(String tip) {Scanner scanner = new Scanner(System.in);StringBuilder help = new StringBuilder();help.append("请输入" + tip + ":");System.out.println(help.toString());if (scanner.hasNext()) {String ipt = scanner.next();if (StringUtils.isNotEmpty(ipt)) {return ipt;}}throw  new MybatisPlusException("请输入正确的" + tip + "!");}public static void main(String[] args) {// 代码生成器AutoGenerator mpg = new AutoGenerator();// 全局配置GlobalConfig gc = new GlobalConfig();String projectPath = System.getProperty("user.dir");gc.setOutputDir(projectPath + "/src/main/java");//作者gc.setAuthor("mozz");//打开输出目录gc.setOpen(false);//xml开启 BaseResultMapgc.setBaseResultMap(true);//xml 开启BaseColumnListgc.setBaseColumnList(true);// 实体属性 Swagger2 注解gc.setSwagger2(true);mpg.setGlobalConfig(gc);// 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/t_music? useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia" + "/Shanghai");dsc.setDriverName("com.mysql.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("root");mpg.setDataSource(dsc);// 包配置PackageConfig pc = new PackageConfig();pc.setParent("com.example.music").setEntity("entity").setMapper("mapper").setService("service").setServiceImpl("service.impl").setController("controller");mpg.setPackageInfo(pc);// 自定义配置InjectionConfig cfg = new InjectionConfig() {@Overridepublic void initMap() {// to do nothing}};// 如果模板引擎是 freemarkerString templatePath = "/templates/mapper.xml.ftl";// 如果模板引擎是 velocity// String templatePath = "/templates/mapper.xml.vm";// 自定义输出配置List<FileOutConfig> focList = new ArrayList<>();// 自定义配置会被优先输出focList.add(new FileOutConfig(templatePath) {@Overridepublic String outputFile(TableInfo tableInfo) {// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会return projectPath + "/src/main/resources/mapper/"+ tableInfo.getEntityName() + "Mapper"+ StringPool.DOT_XML;}});cfg.setFileOutConfigList(focList);mpg.setCfg(cfg);// 配置模板TemplateConfig templateConfig = new TemplateConfig();templateConfig.setXml(null);mpg.setTemplate(templateConfig);// 策略配置StrategyConfig strategy = new StrategyConfig();//数据库表映射到实体的命名策略strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略strategy.setColumnNaming(NamingStrategy.no_change);//lombok模型strategy.setEntityLombokModel(true);//生成 @RestController 控制器strategy.setRestControllerStyle(true);strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));strategy.setControllerMappingHyphenStyle(true);//表前缀
//        strategy.setTablePrefix("t_");mpg.setStrategy(strategy);mpg.setTemplateEngine(new FreemarkerTemplateEngine());mpg.execute();}
}

文件自动生成,随后进行代码开发,有些是通过mybaits-plus实现的。

整体目录如下:

返回结果接口及封装类

package com.example.music.commons;public interface ResultCode {public static Integer SUCCESS = 20000;public static Integer ERROR = 20001;}
package com.example.music.commons;//import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.HashMap;
import java.util.Map;//统一返回结果的类
@Data
public class R {@ApiModelProperty(value = "是否成功")private boolean success;@ApiModelProperty(value = "返回码")private Integer code;@ApiModelProperty(value = "返回消息")private String message;@ApiModelProperty(value = "返回数据")private Map<String,Object> data = new HashMap<String,Object>();//构造方法私有private R(){};//成功的静态方法public static R ok(){R r = new R();r.setSuccess(true);r.setCode(ResultCode.SUCCESS);r.setMessage("成功");return r;}//失败的静态方法public static R error(){R r = new R();r.setSuccess(false);r.setCode(ResultCode.ERROR);r.setMessage("失败");return r;}public R success(Boolean success){this.setSuccess(success);return this;}public R code(Integer code){this.setCode(code);return this;}public R message(String message){this.setMessage(message);return this;}public R data(String s,Object o){this.data.put(s,o);return this;}public R data(Map<String,Object> map){this.setData(map);return this;}
}

swagger配置类

package com.example.music.config;import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;@Configuration
@EnableSwagger2
public class SwaggerConfig {@Beanpublic Docket webApiConfig(){return new Docket(DocumentationType.SWAGGER_2).groupName("webApi").apiInfo(webApiInfo()).select().paths(Predicates.not(PathSelectors.regex("/admin/.*"))).paths(Predicates.not(PathSelectors.regex("/error.*"))).build();}private ApiInfo webApiInfo(){return new ApiInfoBuilder().title("API文档").description("接口定义").version("1.0").contact(new Contact("mozz", "http://www.baidu.com","1345656307@qq.com")).build();}
}

异常类

package com.example.music.expection;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class MusicExpection extends RuntimeException{private Integer code; //状态码private String msg; //异常信息}
package com.example.music.expection;import com.example.music.commons.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;@ControllerAdvice
@Slf4j
public class GlobalExpectionHandler {//指定出现什么异常执行这个方法@ExceptionHandler(Exception.class)@ResponseBody  //为了返回数据public R error(Exception e){e.printStackTrace();return R.error().message("执行了全局异常");}@ExceptionHandler(MethodArgumentTypeMismatchException.class)@ResponseBody  //为了返回数据public R error(MethodArgumentTypeMismatchException e){e.printStackTrace();return R.error().message("执行了MethodArgumentTypeMismatchException异常");}//自定义异常@ExceptionHandler(MusicExpection.class)@ResponseBody  //为了返回数据public R error(MusicExpection e){log.error(e.getMessage());e.printStackTrace();return R.error().code(e.getCode()).message(e.getMsg());}}

实体类

package com.example.music.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;/*** <p>* * </p>** @author mozz* @since 2022-09-22*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="Singer对象", description="")
public class Singer implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String name;private String nickname;private Integer sex;private String pic;private LocalDateTime birth;private String address;private String introduction;private String isDelete;}

创建一个vo目录,目录下创建一个PageResult实体,用来对分页进行封装

package com.example.music.vo;import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(description = "分页对象")
public class PageResult<T> {private List<T> records;  //分页列表private Integer count; //总数}

1.controller(包括分页查询,id查询,修改,删除,批量删除等功能)

package com.example.music.controller;import com.example.music.commons.R;
import com.example.music.entity.Singer;
import com.example.music.service.ISingerService;
import com.example.music.vo.PageResult;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** <p>*  前端控制器* </p>** @author mozz* @since 2022-09-22*/
@RestController
@RequestMapping("/music/singer")
@CrossOrigin
public class SingerController {@Autowiredprivate ISingerService singerService;/*** 分页查询所有歌手信息* @param page* @param limit* @return*/@ApiOperation(value = "分页查询所有歌手信息")@GetMapping("lists/{page}/{limit}")public R getAllSinger(@ApiParam(name = "page", value = "当前页码", required = true)@PathVariable("page") Long page,@ApiParam(name = "limit", value = "每页记录数", required = true)@PathVariable("limit") Long limit){page = (page-1)*limit;PageResult<Singer> singers = singerService.getAllSinger(page,limit);return R.ok().data("data",singers);}/*** 关键字模糊查询* @param keyword* @return*/@ApiOperation(value = "关键字模糊查询")@GetMapping("{keyword}/{page}/{limit}")public R getSingerByKeyword(@PathVariable("keyword") String keyword,@PathVariable("page") Long page,@PathVariable("limit") Long limit){page = (page-1)*limit;PageResult<Singer> singers = singerService.getSingerByKeyword(keyword,page,limit);return R.ok().data("data",singers);}/*** 根据id查询歌手信息* @param id* @return*/@ApiOperation(value = "根据id查询")@GetMapping("/info/{id}")public R getSingerById(@PathVariable("id") Integer id){Singer singer = singerService.getSingerById(id);return R.ok().data("data",singer);}/*** 根据id删除歌手信息* @param id* @return*/@ApiOperation(value = "根据id删除歌手信息")@PostMapping("delete/{id}")public R deleteSinger(@PathVariable("id") Integer id){singerService.deleteSinger(id);return R.ok();}/*** 批量删除歌手信息* @param ids* @return*/@ApiOperation(value = "批量删除歌手信息")@PostMapping("/batchDelete/{ids}")public R batchDeleteSinger(@PathVariable("ids") Integer[] ids){singerService.batchDeleteSinger(ids);return R.ok();}/*** 添加歌手信息* @param singer* @return*/@ApiOperation(value = "添加歌手信息")@PostMapping("addSinger")public R addSinger(@RequestBody Singer singer){singerService.addSinger(singer);return R.ok().data("data",singer);}/*** 修改歌手信息* @param singer* @return*/@ApiOperation(value = "修改歌手信息")@PostMapping("update")public R updateSinger(@RequestBody Singer singer){singerService.updateSinger(singer);return R.ok().data("data",singer);}}

2.service

package com.example.music.service;import com.example.music.entity.Singer;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.music.vo.PageResult;import java.util.List;/*** <p>*  服务类* </p>** @author mozz* @since 2022-09-22*/
public interface ISingerService extends IService<Singer> {PageResult<Singer> getAllSinger(Long page, Long limit);PageResult<Singer> getSingerByKeyword(String keyword,Long page, Long limit);Singer getSingerById(Integer id);void deleteSinger(Integer id);void batchDeleteSinger(Integer[] ids);void addSinger(Singer singer);void updateSinger(Singer singer);
}

实现类

package com.example.music.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.music.entity.Singer;
import com.example.music.expection.MusicExpection;
import com.example.music.mapper.SingerMapper;
import com.example.music.service.ISingerService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.music.vo.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;/*** <p>*  服务实现类* </p>** @author mozz* @since 2022-09-22*/
@Service
public class SingerServiceImpl extends ServiceImpl<SingerMapper, Singer> implements ISingerService {@Resourceprivate SingerMapper singerMapper;@Overridepublic PageResult<Singer> getAllSinger(Long page, Long limit) {//获取所有歌手信息List<Singer> singers = singerMapper.getAllSinger(page,limit);PageResult<Singer> pageResult = new PageResult<>();pageResult.setRecords(singers);//总记录数Integer count = singerMapper.selectList(null).size();pageResult.setCount(count);return pageResult;}@Overridepublic PageResult<Singer> getSingerByKeyword(String keyword,Long page, Long limit) {List<Singer> singers = singerMapper.getSingerByKeyword(keyword,page,limit);PageResult<Singer> result = new PageResult<>();result.setRecords(singers);QueryWrapper wrapper = new QueryWrapper();wrapper.like("name",keyword);Integer count = singerMapper.selectList(wrapper).size();result.setCount(count);return result;}@Overridepublic Singer getSingerById(Integer id) {QueryWrapper<Singer> wrapper = new QueryWrapper();wrapper.eq("id",id);wrapper.eq("is_delete","0");Singer singer = singerMapper.selectOne(wrapper);return singer;}@Overridepublic void deleteSinger(Integer id) {singerMapper.deleteSinger(id);}@Overridepublic void batchDeleteSinger(Integer[] ids) {singerMapper.batchDeleteSinger(ids);}@Overridepublic void addSinger(Singer singer) {//判断是否有该歌手信息QueryWrapper<Singer> wrapper = new QueryWrapper<>();wrapper.eq("is_delete","0");List<Singer> singers = singerMapper.selectList(wrapper);String name = singer.getName();for(Singer item: singers){if(name.equals(item.getName())){throw new MusicExpection(20000,"该歌手信息已存在");}}singerMapper.insert(singer);}@Overridepublic void updateSinger(Singer singer) {QueryWrapper<Singer> wrapper = new QueryWrapper<>();wrapper.eq("id",singer.getId());singerMapper.update(singer, wrapper);}
}

3.mapper

package com.example.music.mapper;import com.example.music.entity.Singer;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;import java.util.List;/*** <p>*  Mapper 接口* </p>** @author mozz* @since 2022-09-22*/
public interface SingerMapper extends BaseMapper<Singer> {List<Singer> getAllSinger(@Param("current") Long page, @Param("size") Long limit);void deleteSinger(@Param("id") Integer id);void batchDeleteSinger(Integer[] ids);List<Singer> getSingerByKeyword(@Param("keyword")String keyword,@Param("current")Long page,@Param("size")Long limit);
}

4.xml(有几个sql没用mybatis-plus,mybatis-plus用的不太熟,所以自己写的)

<?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.example.music.mapper.SingerMapper"><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="com.example.music.entity.Singer"><id column="id" property="id" /><result column="name" property="name" /><result column="sex" property="sex" /><result column="pic" property="pic" /><result column="birth" property="birth" /><result column="location" property="address" /><result column="introduction" property="introduction" /></resultMap><!-- 通用查询结果列 --><sql id="Base_Column_List">id, name, sex, pic, birth, location, introduction</sql><select id="getAllSinger" resultType="com.example.music.entity.Singer">select `id`,`name`,nickname,sex,pic,birth,address,introduction,is_deletefrom singer where is_delete = '0' order by id ASC limit #{current},#{size}</select><select id="getSingerByKeyword" resultType="com.example.music.entity.Singer">select `id`,`name`,nickname,sex,pic,birth,address,introduction,is_deletefrom singer where is_delete = '0' and `name` like concat ('%',#{keyword},'%')order by id ASC limit #{current},#{size}</select><update id="deleteSinger" parameterType="integer">update singer set is_delete = '1' where id=#{id}</update><update id="batchDeleteSinger">update singer t set t.is_delete = '1' wheret.id in<foreach item="ids" collection="array" index="index" open="("separator="," close=")">#{ids}</foreach></update></mapper>

至此整个后台的增删改查全部完成,启动类不要忘记@MapperScan去映射mapper文件

通过swagger进行测试:

可以看到所有的接口都通过swagger展示出来,去对应的接口中进行测试即可,我这边通过测试都已经成功,接下来就进行前端页面联调开发了。

4.歌手信息前台开发:

本次项目使用的是vue-admin-template模板进行开发,通过nginx进行端口代理,nginx需要修改一下nginx.conf文件中的地址信息。(这个模板我自己改动过)

其中8888就是你yml或者properties文件中设置的端口号,music就是你统一的路径地址。

首先进行前后端联调,联调成功后具体页面如下:

出现该页面说明前后端联调成功,下面进行具体的开发。

(1)首先左侧菜单应该包含用户管理、歌手管理、歌曲管理以及评论管理

该部分主要通过前端路由实现,可以看到,设置好后页面效果如下:

随后就可以在对应的页面进行开发了,路由代码如下:

import Vue from 'vue'
import Router from 'vue-router'// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loadingVue.use(Router)/* Layout */
import Layout from '../views/layout/Layout'/**
* hidden: true                   if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true               if set true, will always show the root menu, whatever its child routes length
*                                if not set alwaysShow, only more than one route under the children
*                                it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect           if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name'             the name is used by <keep-alive> (must set!!!)
* meta : {title: 'title'               the name show in submenu and breadcrumb (recommend set)icon: 'svg-name'             the icon show in the sidebar,}
**/
export const constantRouterMap = [{ path: '/login', component: () => import('@/views/login/index'), hidden: true },{ path: '/404', component: () => import('@/views/404'), hidden: true },{path: '/',component: Layout,redirect: '/dashboard',name: 'Dashboard',hidden: true,children: [{path: 'dashboard',component: () => import('@/views/dashboard/index')}]},{path: '/test',component: Layout,name: 'test',alwaysShow:true,meta: { title: '用户管理', icon: 'form' },children: [{path: 'userList',name: 'userList',component: () => import('@/views/user/userList'),meta: { title: '用户列表', icon: 'form' },}]},{path: '/singer',component: Layout,name: 'singer',alwaysShow:true,meta: { title: '歌手管理', icon: 'form' },children: [{path: 'singerList',name: 'singerList',component: () => import('@/views/singer/singerList'),meta: { title: '歌手列表', icon: 'form' },},]},{path: '/song',component: Layout,name: 'song',alwaysShow:true,meta: { title: '歌曲管理', icon: 'form' },children: [{path: 'songList',name: 'songList',component: () => import('@/views/song/songList'),meta: { title: '歌曲列表', icon: 'form' },}]},{path: '/comment',component: Layout,name: 'comment',alwaysShow:true,meta: { title: '评论管理', icon: 'form' },children: [{path: 'commentList',name: 'commentList',component: () => import('@/views/comment/commentList'),meta: { title: '评论列表', icon: 'form' },}]},//  {//     path: '/home',//     component: Layout,//     name: 'home',//     meta: { title: '首页', icon: 'form' },//     children: []//   },{ path: '*', redirect: '/404', hidden: true }
]export default new Router({// mode: 'history', //后端支持可开scrollBehavior: () => ({ y: 0 }),routes: constantRouterMap
})

然后进入到歌手列表进行页面开发,具体如下

 基础的增删改查就是这些了,然后就是如何从后台获取数据了。

前端请求后端接口全部统一写在了api目录下

singer.js文件

import request from '@/utils/request'export default {//查询所有歌手getAllSingers(current, limit){return request({url: `/music/singer/lists/${current}/${limit}`,method: 'get'})},//批量删除batchdeleteSinger(ids){return request({url:'/music/singer/batchDelete/'+ids,method: 'post'})},//id删除deleteSinger(id){return request({url:'/music/singer/delete/'+id,method: 'post'})},//根据姓名模糊查询getSingerByKeyword(keyword,current, limit){return request({url: `/music/singer/${keyword}/${current}/${limit}`,method: 'get'})},//添加歌手addSinger(singer) {return request({url: `/music/singer/addSinger`,method: 'post',data: singer})},// 修改歌手信息updateSinger(singer){return request({url:`/music/singer/update`,method: 'post',data: singer})},}

然后在singerList.vue中写页面,如下:

<template><div class="app-container"><h3>歌手列表</h3><el-button type="danger" @click="batchdelete":disabled="this.sels.length === 0">批量删除</el-button><el-button type="primary" >新增</el-button><el-inputstyle="width:20%;margin-top: 10px;"placeholder="请输入姓名模糊查询"prefix-icon="el-icon-search"clearablev-model="keyword"></el-input><el-button type="primary" icon="el-icon-search" @click="loadListData()">搜索</el-button><el-table:data="tableData"borderfithighlight-current-rowstyle="width: 100%;margin-top: 20px;"@selection-change="handleSelectionChange"><el-table-columntype="selection"borderwidth="55"></el-table-column><el-table-columnlabel="姓名"width="120"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.name }}</span></template></el-table-column><el-table-columnlabel="昵称"width="120"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.nickname }}</span></template></el-table-column><el-table-columnlabel="性别"width="80"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.sex == 0 ? '女' : '男' }}</span></template></el-table-column><el-table-columnlabel="出生日期"prop="birth"sortablewidth="180"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.birth }}</span></template></el-table-column><el-table-columnlabel="地址"width="120"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.address }}</span></template></el-table-column><el-table-columnlabel="简介"width="180"><template slot-scope="scope"><span style="margin-left: 10px"><!-- {{ scope.row.introduction }} --></span></template></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-buttonsize="mini"type="success" plain@click="handleEdit(scope.$index, scope.row)">编辑</el-button><el-buttonsize="mini"type="danger"@click="handleDelete(scope.row.id)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><div style="text-align: center;margin-top: 10px;"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="page":page-sizes="[5, 10, 15, 20]":page-size="limit"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div></div></template><script>import singer from '@/api/singer/singer'export default {data() {return {sels:[],keyword:'',tableData: [],page: 1,//开始页limit: 5, //每页记录数total: 0, //总记录数}},watch:{keyword(newValue){if(!newValue){this.loadListData();}}},created() {this.getList();},methods: {handleEdit(index, row) {console.log(index, row);},handleDelete(id) {this.$confirm('此操作将永久删除,是否继续?','提示',{confirmButtonText:'确定',cancelButtonText: '取消',type:'warning'}).then(()=>{singer.deleteSinger(id).then(res => {if(res){this.$message.success("删除成功")this.getList()}else{this.$message.success("删除失败")}})}).catch(() => {})},// this.manageCheckPlanDetailTableData 表格数据数组handleSelectionChange(sels) {this.sels = sels;// console.log("选中的值",sels.map((item) => item.id));},handleSelectionChange(sels) {this.sels = sels;// console.log("选中的值",sels.map((item) => item.id));},handleCurrentChange(page) {this.page = page;// console.log("page:",this.page)if(this.keyword){this.searchInfo();}else{this.getList();}},handleSizeChange(limit){this.limit = limit;if(this.keyword){this.searchInfo();}else{this.getList();}},loadListData() {if (this.keyword) {// 调用模糊查询接口this.searchInfo();} else{// 调用全部接口this.getList();}},getList(){singer.getAllSingers(this.page,this.limit).then(res => {console.log(res.data.data.records)this.tableData=res.data.data.recordsthis.total=res.data.data.count})},searchInfo(){singer.getSingerByKeyword(this.keyword,this.page,this.limit).then(res => {console.log(res.data.data);this.tableData=res.data.data.recordsthis.total=res.data.data.count})},batchdelete(){let ids = []this.$confirm('此操作将永久删除,是否继续?','提示',{confirmButtonText:'确定',cancelButtonText: '取消',type:'warning'}).then(()=>{let ids = this.sels.map((item) => item.id);const _this = thissinger.batchdeleteSinger(ids).then(function(resp){_this.$message({type: 'success',message: '删除成功!'});_this.getList()})}).catch(() => {})},}}</script><style>
</style>

这时可以看到基本的信息功能差不多完成了。

还差一个新增和编辑,通过弹框实现,继续开发。

新增和编辑功能完成,整个的前端页面代码如下:

<template><div class="app-container"><h3>歌手列表</h3><el-button type="danger" @click="batchdelete":disabled="this.sels.length === 0">批量删除</el-button><el-button type="primary" @click="addSinger">新增</el-button><el-inputstyle="width:20%;margin-top: 10px;"placeholder="请输入姓名模糊查询"prefix-icon="el-icon-search"clearablev-model="keyword"></el-input><el-button type="primary" icon="el-icon-search" @click="loadListData()">搜索</el-button><el-table:data="tableData"borderfithighlight-current-rowstyle="width: 100%;margin-top: 20px;"@selection-change="handleSelectionChange"><el-table-columntype="selection"borderwidth="55"></el-table-column><el-table-columnlabel="姓名"width="120"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.name }}</span></template></el-table-column><el-table-columnlabel="昵称"width="120"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.nickname }}</span></template></el-table-column><el-table-columnlabel="性别"width="80"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.sex == 0 ? '女' : '男' }}</span></template></el-table-column><el-table-columnlabel="出生日期"prop="birth"sortablewidth="180"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.birth }}</span></template></el-table-column><el-table-columnlabel="地址"width="120"><template slot-scope="scope"><span style="margin-left: 10px">{{ scope.row.address }}</span></template></el-table-column><el-table-columnlabel="简介"width="180"><template slot-scope="scope"><span style="margin-left: 10px"><!-- {{ scope.row.introduction }} --></span></template></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-buttonsize="mini"type="success" plain@click="handleEdit(scope.row.id)">编辑</el-button><el-buttonsize="mini"type="danger"@click="handleDelete(scope.row.id)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><div style="text-align: center;margin-top: 10px;"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="page":page-sizes="[5, 10, 15, 20]":page-size="limit"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div><el-dialog title="歌手信息" :visible.sync="dialogTableVisible" ><el-form :model="singer" :rules="rules" ref="singer"><el-form-item label="姓名" :label-width="formLabelWidth" prop="name"><el-input v-model="singer.name"></el-input></el-form-item><el-form-item label="昵称" :label-width="formLabelWidth" prop="sex"><el-input v-model="singer.nickname" autocomplete="off"></el-input></el-form-item><el-form-item label="性别" :label-width="formLabelWidth"><!-- <el-input v-model="singer.sex" autocomplete="off"></el-input> --><el-radio-group v-model="singer.sex"><el-radio :label="1">男</el-radio><el-radio :label="0">女</el-radio></el-radio-group></el-form-item><el-form-item label="出生日期" :label-width="formLabelWidth" prop="birth"><el-input v-model="singer.birth" autocomplete="off"></el-input></el-form-item><el-form-item label="地址" :label-width="formLabelWidth"><el-input v-model="singer.address" autocomplete="off"></el-input></el-form-item><el-form-item label="简介" :label-width="formLabelWidth"><el-input v-model="singer.introduction" type="textarea" autocomplete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogTableVisible = false">取 消</el-button><el-button type="primary" @click="saveOrUpdate">确 定</el-button></div></el-dialog></div></template><script>import singer from '@/api/singer/singer'export default {data() {return {id:'',  singer:{id:'',name:'',nickname:'',sex:'',birth:'',address:'',introduction:''},sels:[],keyword:'',dialogTableVisible:false,dialogFormVisible:false,tableData: [],page: 1,//开始页limit: 5, //每页记录数total: 0, //总记录数formLabelWidth: '100px',rules: {name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],birth: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }]}}},watch:{keyword(newValue){if(!newValue){this.loadListData();}}},created() {this.getList();},methods: {addSinger(){this.dialogTableVisible=true;this.singer.name = '';this.singer.nickname = '';this.singer.sex = '';this.singer.birth='';this.singer.address='';this.singer.introduction='';},handleEdit(id) {this.dialogTableVisible=true;singer.getSingerById(id).then(res => {console.log("id:",res.data.data)this.singer = res.data.data})},handleDelete(id) {this.$confirm('此操作将永久删除,是否继续?','提示',{confirmButtonText:'确定',cancelButtonText: '取消',type:'warning'}).then(()=>{singer.deleteSinger(id).then(res => {if(res){this.$message.success("删除成功")this.getList()}else{this.$message.success("删除失败")}})}).catch(() => {})},// this.manageCheckPlanDetailTableData 表格数据数组handleSelectionChange(sels) {this.sels = sels;// console.log("选中的值",sels.map((item) => item.id));},handleSelectionChange(sels) {this.sels = sels;// console.log("选中的值",sels.map((item) => item.id));},handleCurrentChange(page) {this.page = page;// console.log("page:",this.page)if(this.keyword){this.searchInfo();}else{this.getList();}},handleSizeChange(limit){this.limit = limit;if(this.keyword){this.searchInfo();}else{this.getList();}},loadListData() {if (this.keyword) {// 调用模糊查询接口this.searchInfo();} else{// 调用全部接口this.getList();}},getList(){singer.getAllSingers(this.page,this.limit).then(res => {console.log(res.data.data.records)this.tableData=res.data.data.recordsthis.total=res.data.data.count})},searchInfo(){singer.getSingerByKeyword(this.keyword,this.page,this.limit).then(res => {console.log(res.data.data);this.tableData=res.data.data.recordsthis.total=res.data.data.count})},batchdelete(){let ids = []this.$confirm('此操作将永久删除,是否继续?','提示',{confirmButtonText:'确定',cancelButtonText: '取消',type:'warning'}).then(()=>{let ids = this.sels.map((item) => item.id);const _this = thissinger.batchdeleteSinger(ids).then(function(resp){_this.$message({type: 'success',message: '删除成功!'});_this.getList()})}).catch(() => {})},saveSinger(){this.singer.id = this.id;singer.addSinger(this.singer).then(res => {console.log("data",res.data.data)this.dialogTableVisible=false;this.$message({type: 'success',message: '添加歌手成功!'});this.getList();})},updateSinger(){singer.updateSinger(this.singer).then(res => {this.dialogTableVisible = false;this.$message({type: 'success',message: '修改歌手信息成功!'});this.getList()})},saveOrUpdate(){if(!this.singer.id){this.saveSinger()}else{this.updateSinger()}}}}</script><style>
</style>

至此整个的springboot基础的前后端增删改查就完成了。

继续更:

由于增删改查完成,涉及到表单功能,比如新增与删除功能,需要对其进行一系列的校验,这边前端是通过设置rules规则完成校验,必要时配合正则表达式。同样后端也需要进行校验,后端采用JSR303校验

JSR303校验

首先引入以下依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>2.2.6.RELEASE</version></dependency><dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>2.0.1.Final</version></dependency>

然后在controller层需要添加@Valid注解,并且传入BindingResult,比如修改歌手信息,歌手名不能为空,在实体类上添加相应的注解

package com.example.music.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.Date;import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;/*** <p>* * </p>** @author mozz* @since 2022-09-22*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="Singer对象", description="")
public class Singer implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;@NotBlank(message = "用户名不能为空")private String name;private String nickname;@NotNullprivate Integer sex;private String pic;@JsonFormat(pattern = "yyyy-MM-dd")@NotNull(message = "用户出生日期不能为空")private Date birth;private String address;private String introduction;private String isDelete;}

controller中:

 /*** 修改歌手信息* @param singer* @return*/@ApiOperation(value = "修改歌手信息")@PostMapping("update")public R updateSinger(@RequestBody @Valid Singer singer, BindingResult result){if(result.hasErrors()){Map<String,String> map = new HashMap<>();result.getFieldErrors().forEach((item) -> {String message = item.getDefaultMessage();String field = item.getField();map.put(field,message);});return R.error().data("data",map);}singerService.updateSinger(singer);return R.ok().data("data",singer);}

最后运行swagger验证:让姓名name为空查看结果

可以看到,返回的信息就是我们实体类上注解对应的message信息,同样的增加功能也需要进行对用的校验操作。至此校验完成。

springboot/vue前后端分离后台管理系统增删改查相关推荐

  1. 新B站视频来了!Spring security + vue前后端分离后台管理系统

    距离上次发布视频已经很久了哈,有点懒.这次发布的是一个基于Spring security + jwt + vue的前后端分离后台管理系统VueAdmin,项目讲解视频一共54集,共800分钟,发布到B ...

  2. 一款小清新的 SpringBoot+ Mybatis 前后端分离后台管理系统项目

    今日推荐 推荐3个快速开发平台 前后端都有 项目经验又有着落了推荐一个高仿微信的项目 有点屌!!一二线城市知名 IT 互联网公司名单(新版) 项目介绍 前后端分离架构,分离开发,分离部署,前后端互不影 ...

  3. 一款小清新的 SpringBoot+ Mybatis 前后端分离后台管理系统

    往期热门文章: 1.分布式数据一致性思考-B端系统一致性 2.Java字符串拼接的五种方法,哪种性能最好? 3.一次线上JVM调优实践,FullGC40次/天到10天一次的优化过程 4.Chrome ...

  4. springBoot加layui和mybatis后台管理系统增删改查分页登录注销修改密码功能

    超市订单管理系统 1 登录页面 1.1 登录 点击提交按钮提交form表单使用post请求把(String name, String password)数据传到后台loginController 路径 ...

  5. php增删改查前后端分离,前后端分离之前端增删改查

    初次接触前后端分离,现把前端一个例子放出来记录以下,不喜勿喷. html静态页面代码: 菜单管理平台数据... 菜单管理查询条件列表 菜单名称: 菜单父级: 菜单名称: 菜单级别: 菜单父级: 图片地 ...

  6. Springboot Vue前后端分离实现基础的后台管理系统

    最近在利用空闲的时间学习springboot+Vue前后端分离相关知识,然后动手写了个后台管理系统,实现登录验证.学生模块.英雄联盟模块.数据可视化(暂未开发,准备使用echarts进行):这边先以英 ...

  7. 基于springboot vue前后端分离的图书借阅管理系统源码

    请观看视频: 基于springboot vue前后端分离的图书借阅管理系统源码 <project xmlns="http://maven.apache.org/POM/4.0.0&qu ...

  8. 基于springboot+vue前后端分离的学生在线考试管理系统

    一.基于springboot+vue前后端分离的学生在线考试管理系统 本系统通过教师用户创建班级编写试卷信息然后发布到班级.学生用户进入班级,在线作答,考试结果数据通过网络回收,系统自动进行判分,生成 ...

  9. 基于SSM+SpringBoot+Vue前后端分离的高校大学生毕业设计管理系统

    大家好,很高兴和大家分享源码.不管是什么样的需求.都希望各位计算机专业的同学们有一个提高. 大家可以通过常用的搜索引擎,以百度为例,搜索 源码乐园 code51 ,然后再次搜索 自己想要的即可.更多的 ...

最新文章

  1. 比Hadoop快至少10倍的物联网大数据平台,我把它开源了
  2. SphereFace的原理
  3. Tengine 反向代理状态检测
  4. mongoose如何发送html页面,javascript – 如何将HTML插入Mongodb?
  5. graphpad7.04多组比较p值_同是折线图为何你却这么优秀,这才是多组数据作图应该有的样子...
  6. Python 进程锁使用
  7. 秋色园QBlog技术原理解析:性能优化篇:打印页面SQL,全局的SQL语句优化(十三)...
  8. 利用 Python 优雅地将 PDF 转换成图片
  9. python 学习(八—1) 项目:生成随机的测试试卷文件
  10. 仿射变换(Affine Transformation)原理及应用(1)
  11. 2060显卡驱动最新版本_GPU驱动CUDAPyTorch对应关系
  12. 孙溟㠭书画艺术《退步向前》
  13. 你真的知道什么是三观吗?
  14. 为什么行程码不能显示到县级,工信部回应
  15. 谈判技巧---From一亩三分地帖子
  16. ETL和ELT到底有啥区别???
  17. Win7 ODBC 数据源ACCESS2003的链接
  18. 创新型中药制药企业苏轩堂将在纳斯达克挂牌交易
  19. 【BZOJ-2295】我爱你啊 暴力
  20. windows下的makefile教程

热门文章

  1. Django连接mysql数据库操作
  2. 风电场气象服务器是什么系统,风电场气象参数采集与管理系统
  3. 正在检测目标单片机...
  4. 论文分享 Deep Distance Transform for Tubular Structure Segmentation in CT Scans
  5. 玩儿转ffmeg的7个技巧
  6. python制作闯关答题软件_闯关答题-可以用做问答互动的软件-闯关答题会议 微信 问答游戏GO互动智能现场...
  7. 详解六种常见的上下文切换场景
  8. SpringBoot是如何动起来的
  9. 无人驾驶大巴试车_中国无人驾驶汽车高速公路试车成功
  10. NFC与其他近距离通信技术的比较