后台搭建
该项目的网站后台中主要有四个模块的功能,分别为流派管理,歌手管理,歌曲管理和专辑管理。
这四个模块可以分别对数据库表中的信息进行查看与修改。
那么书接上回,下面开始进行后台的搭建
首先要引入网站后台的界面,在hami_console子模块下的WEB-INF文件夹下创建page文件夹,把jsp页面拷贝到page下
并且把所有静态资源放入代webapp文件夹下,完成后整体结构如下:
在该子项目的resources文件夹下创建springmvc.xml文件来对springmvc进行配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--类型转换器-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
<property name="features">
<array>
<value>WriteMapNullValue</value>
<value>WriteNullStringAsEmpty</value>
</array>
</property>
<property name="dateFormat" value="yyyy-MM-dd"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--扫码器组件-->
<context:component-scan base-package="com.qcby.controller"/>
<!--视图解析器:为controller控制类中的返回值增加前缀和后缀-->
<bean id="viewResource" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--有关于文件上传的组件配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10240000"></property>
</bean>
<!--开启这个组件,默认可以不写-->
<mvc:default-servlet-handler/>
<!--配置一个初始化的访问的controller-->
<mvc:view-controller path="/index" view-name="index"/>
</beans>
配置web.xml
在wabapp文件夹下的web.xml中配置springmvc
?xml version="1.0" encoding="UTF-8"?>
<web-app
version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!--拦截所有请求进入springMVC-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<filter>
<filter-name>filter1</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
启动服务器访问后台index首页
流派模块开发
列表查询
在该模块中采用分页查询,将数据库中流派的信息通过分页进行查询,期望查询结果如下:
想要达成该效果需要在后端查询到的数据呈现到前端的jsp页面上。
首先在后端中要把查询结果包装到一起,因此在hami_core子模块中的util文件夹下创建Page类来存放查询到的数据,该类包括每页展示数量(已知),当前的页号,总页数,开始页数,数据集合结果和总记录数。使用该类的一个对象将其返回到前端界面即可。
该类信息如下;
/**
* 封装前端需要的承载数据以及分页相关的一个实体
* 自定义页的类
*/
public class Page<T> {
//每页展示数量(已知)
private Integer pageSize=5;
//页码(已知)
private Integer pageNo=1;
//总页数(计算) 总记录数/每页展示数量
private Integer totalPage;
//开始行号(计算)(pageNO-1) *pagesize
private Integer startNum=0;
//数据集合结果
private List<T> list;
//总记录数 count(*)
private Integer totalCount=0;
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getPageNo() {
return pageNo;
}
public void setPageNo(Integer pageNo) {
this.pageNo = pageNo;
}
public Integer getTotalPage() {
totalPage=totalCount/pageSize;
if(totalCount==0 || totalCount%pageSize!=0){
totalPage++;
}
return totalPage;
}
public void setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
}
public Integer getStartNum() {
return startNum;
}
public void setStartNum(Integer startNum) {
this.startNum = startNum;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
}
将后端查询到的实体返回后前端界面需要接收,在jsp界面中采用foreach进行接收,代码如下:
<tbody>
<c:forEach items="${page.list}" var="mtype" varStatus="status">
<tr>
<td class="hidden-xs-portrait">${mtype.tid}</td>
<td class="hidden-xs-portrait">${mtype.tname}</td>
<td class="hidden-xs"> ${mtype.tdesc} </td>
<td>
<button class="btn btn-sm btn-primary" type="button" modify tid="${mtype.tid}" > 修改</button>
<button data-toggle="button" class="btn btn-sm btn-warning" tid="${mtype.tid}"> 删除</button>
</td>
</tr>
</c:forEach>
</tbody>
<jsp:include page="pagination.jsp"></jsp:include>
为节省代码,将分页的页码单独写一个界面pagination.jsp,并在其他界面中引用
<div class="clearfix text-right">
<%--隐藏域--%>
<input type="hidden" id="pageNo" name="pageNo" value="${mq.pageNo}">
<input type="hidden" id="totalPage" value="${page.totalPage}">
<ul class="pagination no-margin">
<li id="prev" class="disabled"><a href="#">Prev</a></li>
<c:forEach begin="1" end="${page.totalPage}" var="myPageNo">
<li <c:if test="${myPageNo == mq.pageNo}">class="active"</c:if>><a
pageNoButton href="#">${myPageNo}</a></li>
</c:forEach>
<li id="next"><a href="#">Next</a></li>
</ul>
</div>
在界面中使用js来控制分页
/**
* 用于控制上一页和下一页的可用的切换
* @type {jQuery}
*/
var pageNo = $("#pageNo").val();
var totalPage = $("#totalPage").val();
pageNo = parseInt(pageNo);
totalPage = parseInt(totalPage);
if (pageNo == 1 && pageNo == totalPage) {
$("#prev").addClass("disabled");
$("#next").addClass("disabled");
}
if (pageNo == 1 && pageNo < totalPage) {
$("#prev").addClass("disabled");
$("#next").removeClass("disabled");
}
if (pageNo > 1 && pageNo < totalPage) {
$("#prev").removeClass("disabled");
$("#next").removeClass("disabled");
}
if (pageNo > 1 && pageNo == totalPage) {
$("#prev").removeClass("disabled");
$("#next").addClass("disabled");
}
$("#prev").click(function () {
$("#pageNo").val(--pageNo);
$("#txForm").submit();
})
$("#next").click(function () {
$("#pageNo").val(++pageNo);
$("#txForm").submit();
})
$("a[pageNoButton]").click(function () {
var pageNo = $(this).html();
$("#pageNo").val(pageNo);
$("#txForm").submit();
})
想要分页查询到流派信息还需要一个流派查询的类,来传递前端传来的参数,例如当前是第几页,要实现搜素功能时要传递的名字
在hami_core的query文件下创建该类如下
/**
* 只作为表现层接收前端参数封装使用
*/
public class MtypeQuery extends Mtype{
//页码
private Integer pageNo;
//每页展示数量
private Integer pageSize=5;
//开始行号
private Integer startNum;
public Integer getPageNo() {
return pageNo;
}
public void setPageNo(Integer pageNo) {
this.pageNo = pageNo;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getStartNum() {
return startNum;
}
public void setStartNum(Integer startNum) {
this.startNum = startNum;
}
}
想要得到分页查询的数据,需要在数据库中查询到具体信息和数量,最后通过在实现类中编写具体方法逻辑将数据包装进一个Page对象中返回u
在MtypeMapper.xml下需要编写sql语句如下:
查询出所有数据和数量并定义查询结果resultMap
<resultMap id="BaseResultMap" type="com.qcby.model.Mtype">
<id column="TID" jdbcType="INTEGER" property="tid" />
<result column="TNAME" jdbcType="VARCHAR" property="tname" />
<result column="TDESC" jdbcType="VARCHAR" property="tdesc" />
</resultMap>
<select id="selectPage" parameterType="com.qcby.query.MtypeQuery" resultMap="BaseResultMap">
SELECT * FROM mtype
<where>
<if test=" tname != null and tname != ''">
tname like '%${tname}%'
</if>
</where>
LIMIT #{startNum},#{pageSize}
</select>
<select id="selectCount" parameterType="com.qcby.query.MtypeQuery" resultType="int">
SELECT count(*) FROM mtype
<where>
<if test=" tname != null and tname != ''"> tname like '%${tname}%' </if>
</where>
</select>
<select id="selectAll" resultType="com.qcby.model.Mtype">
SELECT * from mtype
</select>
写好了sql语句下面就需要定义接口和方法来调用sql语句
由于每个模块中都要用到分页查询,因此编写接口和方法时写在公共方法即可;
在BaseDao接口中定义持久层接口
package com.qcby.dao;
import java.util.List;
/**
* 持久层公共接口
* @param <T>
*/
public interface BaseDao<Q,T> {
int deleteByPrimaryKey(Integer tid);
int insert(T record);
int insertSelective(T record);
T selectByPrimaryKey(Integer tid);
int updateByPrimaryKeySelective(T record);
int updateByPrimaryKey(T record);
List<T> selectPage(Q mq);
Integer selectCount(Q mq);
}
在BaseService中提供相应的分页查询接口
package com.qcby.service;
import com.qcby.util.Page;
/**
* 业务层公共接口
* @param <T> 泛型
*/
public interface BaseService<Q,T> {
int deleteByPrimaryKey(Integer tid);
int insert(T record);
int insertSelective(T record);
T selectByPrimaryKey(Integer tid);
int updateByPrimaryKeySelective(T record);
int updateByPrimaryKey(T record);
//分页查询接口
Page<T> selectByPage(Q mq);
}
实现类:在实现类中编写分页查询的具体逻辑
先通过反射获取pageNo和pageSize,并根据此计算出startNum
之后通过持久层接口调用sql语句查询数据库,最后将查询到的结果放入Page对象中。
public class BaseServiceImpl<Q,T> implements BaseService<Q,T> {
protected BaseDao<Q,T> baseDao;
@Override
public int deleteByPrimaryKey(Integer tid) {
return baseDao.deleteByPrimaryKey(tid);
}
@Override
public int insert(T t) {
return baseDao.insert(t);
}
@Override
public int insertSelective(T t) {
return baseDao.insert(t);
}
@Override
public T selectByPrimaryKey(Integer tid) {
return baseDao.selectByPrimaryKey(tid);
}
@Override
public int updateByPrimaryKeySelective(T t) {
return baseDao.updateByPrimaryKeySelective(t);
}
@Override
public int updateByPrimaryKey(T t) {
return baseDao.updateByPrimaryKey(t);
}
/**
* 多有业务模块的分页业务逻辑处理
* 简单理解:page对象
* 保证page对象各个属性的值按要求返回
* @param mq
* @return
*/
@Override
public Page<T> selectByPage(Q mq){
//1.先准备一个要返回的承载数据的页对象
Page<T> page = new Page<T>();
Class<?> cq = mq.getClass();
try {
//反射调用getPageNo方法拿到pageNo
//获得getPageNo对象
Method getPageNo = cq.getDeclaredMethod("getPageNo", null);
//反射调用getPageNo方法
Integer pageNo = (Integer) getPageNo.invoke(mq,null) ;
//反射调用getPageNo方法拿到pageSize
Method getPageSize = cq.getDeclaredMethod("getPageSize", null);
Integer pageSize = (Integer) getPageSize.invoke(mq,null) ;
//给返回的page设置值
page.setPageNo(pageNo);
page.setPageSize(pageSize);
//设置pageNO 前端给的
//设置pageSize 前端给的
//设置startNum 计算
Method setStartNum = cq.getDeclaredMethod("setStartNum",Integer.class);
setStartNum.invoke(mq,(pageNo-1)*pageSize);
page.setStartNum((pageNo-1)*pageSize);
//查询数据库 调用Mapper baseDao查询满足条件的数据
List<T> list = baseDao.selectPage(mq);
page.setList(list);
//StartNum tname 把结果给page的list设置上
//查询数据库 满足条件的数据总量 page的totalCount设置上值
Integer count=baseDao.selectCount(mq);
page.setTotalCount(count);
}catch (Exception e){
e.printStackTrace();
}
return page;
}
}
至此分页查询的接口和方法编写完毕,接下来就可以在后台模块hami_console的控制类中调用该方法查询到想要的数据并将其返回。
编写MtypeController类:
package com.qcby.controller;
import com.qcby.model.Mtype;
import com.qcby.query.MtypeQuery;
import com.qcby.service.MtypeService;
import com.qcby.util.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Controller将该类交spring管理设为控制类
* @RequestMapping("/mtype") 设置路径来让前端调用
*/
@RequestMapping("/mtype")
@Controller
public class MtypeController {
/**
* 注入流派业务层的对象
* 调用业务层的分页条件查询逻辑
*/
@Autowired
private MtypeService mtypeService;
/**
* 查询流派信息
* 分页 条件 查询
* 流派名称 查看的页码 每页展示数量
* @return
*/
@RequestMapping("/list")
public String list(MtypeQuery mq, Model model){
//1.程序严谨性:判断前端传递的参数
//有没有传递想看第几页,没有设计为访问第一页
if(mq.getPageNo()==null){
mq.setPageNo(1);
}
//2.调用业务层进行分页条件查询
Page<Mtype> page = mtypeService.selectByPage(mq);
//3.返回前端想要的数据 返回一个页对象
model.addAttribute("page",page);
//要进行搜素参数的回显功能
model.addAttribute("mq",mq);
return "mtype";
}
}
在前端界面中采用表单提交的方式来调用后端中的方法,表单中根据路径/mtype/list调用分页查询方法,在搜素时将会重新提交表单,将已有的参数传递给后端重新查询
将要呈现的所有信息都放在表单之中
<form id="txForm" action="/mtype/list" method="post" class="form-horizontal">
<div class="row">
<div class="col-lg-12">
<div class="widget">
<div class="widget-header"><i class="icon-list-ol"></i>
<h3>搜索条件</h3>
</div>
<div class="widget-content">
<fieldset id="find">
<div class="control-group">
<label for="tname" class="control-label">流派</label>
<div class="controls form-group">
<div class="input-group"><span class="input-group-addon"><i
class="icon-music"></i></span>
<input type="text" placeholder="如:摇滚" name="tname" value="${mq.tname}"
id="tname" class="form-control"/>
</div>
</div>
</div>
</fieldset>
<div class="form-actions text-right">
<div>
<button class="btn btn-primary" id="search">搜索</button>
<button id="addSong" class="btn btn-primary" data-target="#myModal2"
type="button">添加流派
</button>
<button id="toggleSearch" flag="2" class="btn btn-default" type="button">收缩↑
</button>
</div>
</div>
</div>
<div class="widget-content">
<div class="body">
<table class="table table-striped table-images"
style="color: white;font-size: 14px">
<thead>
<tr>
<th class="hidden-xs-portrait">序号</th>
<th class="hidden-xs">流派</th>
<th class="hidden-xs">描述</th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${page.list}" var="mtype" varStatus="status">
<tr>
<td class="hidden-xs-portrait">${mtype.tid}</td>
<td class="hidden-xs-portrait">${mtype.tname}</td>
<td class="hidden-xs"> ${mtype.tdesc} </td>
<td>
<button class="btn btn-sm btn-primary" type="button" modify tid="${mtype.tid}" > 修改</button>
<button data-toggle="button" class="btn btn-sm btn-warning" tid="${mtype.tid}"> 删除</button>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<jsp:include page="pagination.jsp"></jsp:include>
</div>
</div>
</div>
</div>
</div>
</form>
最后完善搜素功能
在前端Js中获取输入的名称,点击时将表单提交
$("#search").click(function () {
$("#pageNo").val(1);
$("#txForm").submit();
})
至此,流派模块的分页查询功能完成
流派添加与删除
期待效果:
在前端界面中点击将弹出添加弹出层,输入添加信息后点击确认即可完成添加。点击删除时将该条流派信息删除。
添加功能:
首先在界面中编写弹出层,并让其在点击后弹出
<div id="mtypePop" style="margin-right: 50px;margin-top: 50px; display: none">
<form id="addMtypeForm" class="layui-form" method="post" action="/type/addMtype" lay-filter="example">
<div class="layui-form-item" >
<label class="layui-form-label">输入框</label>
<div class="layui-input-block">
<input type="text" name="tname" style="color: black;" lay-verify="title" autocomplete="off" placeholder="请输入流派名" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">文本域</label>
<div class="layui-input-block">
<textarea style="color: black;" placeholder="请输入流派描述" class="layui-textarea" name="tdesc"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-radius" lay-submit="" lay-filter="demo1">添加流派</button>
</div>
</div>
</form>
</div>
编写JS弹出界面
var pop;
$("#addSong").click(function () {
pop = layer.open({
type: 1,
area:[600, 350],
content: $('#mtypePop')
});
})
添加弹出层也是表单,在JS中编写点击确认后表单提交,调用后端方法
layui.use('form', function(){
var form = layui.form;
//监听提交
form.on('submit(demo1)', function(data){
//layer.msg(JSON.stringify(data.field));
$.ajax({
url:"/mtype/addMtype",
type:"post",
data:data.field,
dataType:"text",
success:function (text) {
if(text == "success"){
layer.msg("添加成功");
layer.close(pop);
}
}
})
return false;
});
});
接下来编写后端方法,实现数据库中的数据添加
由于在逆向工程时已经自动生成了添加删除等方法,因此不用重新编写新的sql语句直接使用即可
因此在MtypeController中编写添加方法
/**
* 添加流派
* @param mtype
* @return
*/
@RequestMapping("/addMtype")
@ResponseBody
public String addMtype(Mtype mtype){
int insert = mtypeService.insert(mtype);
return "success";
}
删除功能;
在JS中编写方法使点击删除删除按钮时调用后端方法,将数据库中的数据删除。通过界面中的tid找到删除的信息,并传递给后端。
$(".btn-warning").click(function () {
var tid = $(this).attr("tid");
ayer.confirm('是否确认删除?', {icon: 3, title:'提示'}, function(index){
$.ajax({
url: "/mtype/delMtype",
type: "post",
data: {
tid:tid
},
ataType: "text",
success: function (text) {
if (text == "success") {
layer.msg("删除成功");
layer.close(index);
$("#txForm").submit();
}
}
)
});
})
在MtypeController中编写删除方法
@RequestMapping("/delMtype")
@ResponseBody
public String delMtype(int tid){
mtypeService.deleteByPrimaryKey(tid);
return "success";
}
至此完成流派的删除功能。
流派修改
期望效果:想要修改流派信息,点击修改按钮后弹出修改弹出层,将该信息所有内容回显,在修改层中进行修改,点击确认后完成修改。
弹出层的DIV:使用添加弹出层一样即可,复制以下修改以下参数就可以使用。
<div id="mtypePop1" style="margin-right: 50px;margin-top: 50px; display: none">
<form id="updateMtypeForm" class="layui-form" method="post" action="/type/updateMtype" lay-filter="example">
<input type="hidden" name="tid" id="tid">
<div class="layui-form-item" >
<label class="layui-form-label">输入框</label>
<div class="layui-input-block">
<input id="ptname" type="text" name="tname" style="color: black;" lay-verify="title" autocomplete="off" placeholder="请输入流派名" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">文本域</label>
<div class="layui-input-block">
<textarea id="ptdesc" style="color: black;" placeholder="请输入流派描述" class="layui-textarea" name="tdesc"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal layui-btn-radius" lay-submit="" lay-filter="demo2">修改流派</button>
</div>
</div>
</form>
</div>
点击修改按钮后弹出界面并调用方法进行回显:
var pop1;
$("[modify]").click(function () {
var tid = $(this).attr("tid");
$.ajax({
url: "/mtype/getMtype",
type: "post",
data: {
tid:tid
},
ataType: "json",
success: function (jsonObj) {
$("#tid").val(jsonObj.tid);
$("#ptname").val(jsonObj.tname);
$("#ptdesc").val(jsonObj.tdesc);
}
})
pop1 = layer.open({
type: 1,
area: [600, 350],
content: $('#mtypePop1')
});
})
点击修改按钮后完成修改
layui.use('form', function () {
var form = layui.form;
//监听提交
form.on('submit(demo2)', function (data) {
//layer.msg(JSON.stringify(data.field));
$.ajax({
url: "/mtype/updateMtype",
type: "post",
data: data.field,
dataType: "text",
success: function (text) {
if (text == "success") {
layer.msg("修改成功");
layer.close(pop1);
$("#txForm").submit();
}
}
})
return false;
});
})
在MtypeController中编写回显方法和修改方法,这两个方法也是逆向工程时就生成好的,直接使用即可
/**
* 流派修改的回显数据方法
*/
@ResponseBody
@PostMapping("/getMtype")
public Mtype getMtype(int tid){
//借助于第三方工具类
Mtype mtype=mtypeService.selectByPrimaryKey(tid);
return mtype;
}
@ResponseBody
@PostMapping("/updateMtype")
public String updateMtype(Mtype mt){
mtypeService.updateByPrimaryKeySelective(mt);
return "success";
}
至此,流派模块的所有功能实现完成,其他模块下一篇文章将会继续说明。