书接上回,我们继续进行哈米音乐项目其他模块的开发,后台剩余的三个模块需要完成对文件的上传(如图片,MP3文件等),因此在开发剩余的三个模块前需要完成对文件服务器的开发,也就是hami_file子模块。
文件服务器
因为是后台模块需要实现文件上传,因此要在hami_console模块中的springmvc文件中添加文件上传的配置。
<!--有关于文件上传的组件配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10240000"></property>
</bean>
value值代表的是上传文件的最大大小,当前设置的是1MB。
随后将自己的tomcat复制一份作为文件服务器
之后修改conf文件下的web.xml文件,将其设置为只读
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
在hami_console中创建控制上传文件的controller
创建UploadController
import com.alibaba.fastjson.JSONObject;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
/**
* 所有业务公用的文件上传的controller
*/
@Controller
@RequestMapping("/upload")
public class UploadController {
/**
* 图片上传的具体方法
*/
@RequestMapping("uploadFile")
public void uploadFile(HttpServletRequest request, HttpServletResponse response, MultipartFile picfile, String fileType,String lastImg)throws Exception{
//1.获取前端传递过来的所有有关于文件上传的参数
//MultipartHttpServletRequest mr = (MultipartHttpServletRequest) request;
//2.获取存储文件相关的内容
//Map<String,MultipartFile> fileMap = mr.getFileMap();
//3.获取文件
//MultipartFile file= fileMap.get("picFile");
//4.获取该文件的字节数组
byte[] bytes = picfile.getBytes();
//5.获取文件的名称
String orignalFilename = picfile.getOriginalFilename();
//6.获取文件名的后缀 产生新名字 05.jpg
String suffix= orignalFilename.substring(orignalFilename.lastIndexOf("."));
//7.定义一个心的名字
String fileName = UUID.randomUUID().toString();
fileName = fileName+suffix;
//8.上传 获取上传的位置 图片服务器的位置 http://localhost:8083/pic/05.jpg
String filePath = "http://localhost:8083";
//绝对路径 http://localhost:8083/pic/05.jpg 上传和显示加载图片
String realPath = filePath+"/"+fileType+"/"+fileName;
//相对路径 pic/05.jpg 存入数据库的那部分
String relativePath = "/"+fileType+"/"+fileName;
//9.上传 jersy客户端进行上传
Client client = Client.create();
//判断
if(lastImg !=null && !"".equals(lastImg)){
WebResource resource1= client.resource(lastImg);
resource1.delete();
}
//获取web资源
WebResource resource= client.resource(realPath);
//上传
resource.put(bytes);
//创建前端需要的json对象
JSONObject jo = new JSONObject();
jo.put("realPath",realPath);
jo.put("relativePath",relativePath);
//响应对象原始方式返回
response.getWriter().write(jo.toString());
}
@RequestMapping("/uploadFileMp3")
public void uploadFileMp3(HttpServletRequest request,HttpServletResponse response,String fileType,String lastMp3) throws IOException {
//1.获取前端传递过来的所有有关于文件上传的参数
MultipartHttpServletRequest mr= (MultipartHttpServletRequest) request;
//2.获取存储文件的相关的内容 map
Map<String, MultipartFile> fileMap = mr.getFileMap();
//3.获取文件
MultipartFile file = fileMap.get("mp3file");
//获取该文件的字节数组
byte[] bytes = file.getBytes();
//处理文件名
String originalFilename = file.getOriginalFilename();
//获取后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//定义唯一的新名字
String fileName = UUID.randomUUID().toString();
fileName=fileName+suffix;
//获取文件上传的路径
String filePath="http://localhost:8083";
//文件上传的绝对路径
String realPath=filePath+"/"+fileType+"/"+fileName;
//获取文件的相对路径
String relativePath="/"+fileType+"/"+fileName;
//9.jersy客户端进行上传
Client client=Client.create();
//判断
if(lastMp3 != null && !"".equals(lastMp3)){
WebResource resource1 = client.resource(lastMp3);
resource1.delete();
}
//10.获取web资源
WebResource resource = client.resource(realPath);
//11.上传
resource.put(bytes);
//创建前端需要的json对象
JSONObject jo=new JSONObject();
jo.put("realPath", realPath);
jo.put("relativePath", relativePath);
//响应对象原始方式返回
response.getWriter().write(jo.toString());
}
}
其中实现了图片文件和mp3文件的上传,因为我当前文件服务器设置的地址是8083,所有上面写的也是8083,可以根据实际需要进行修改,将文件重新命名上传到了服务器上,并返回了绝对地址和相对地址,数据库中储存就是文件的相对地址。
前端在使用时可以发起请求,就可以实现上传功能。
例如:
function submitFile() {
$('#location').val($('#i-file').val());
$("#addAlbumForm").ajaxSubmit({
url:"/upload/uploadFile",
data:{
type:"pic"
},
dataType:"json",
success:function (jo) {
$("#albumPic").attr("src",jo.realPath);
$("#lastImage").val(jo.realPath);
$("#pic").val(jo.relativePath);
}
})
}
专辑开发
专辑查询
由于之前已经使用了逆向工程生成了需要的基本文件,因此这里和前面的流派分页查询类似,唯一用到的新技术是layui的日历插件用于查询
使用如下:
layui.use('laydate', function(){
var laydate = layui.laydate;
//执行一个laydate实例
laydate.render({
elem: '#addPdate'
});
//执行一个laydate实例
laydate.render({
elem: '#searchPdate'
});
});
界面中使用:
<div class="layui-form-item" >
<label class="layui-form-label" style="width:100px">发行时间</label>
<div class="layui-input-block">
<input type="text" id="addPdate" name="pdate" style="color: black; border-color: lightgray;background-color: white" lay-verify="pdate" autocomplete="off" placeholder="请输入发行时间" class="layui-input">
</div>
</div>
前端中依然采用c:foreach来展现分页查询结果
<c:forEach items="${page.list}" var="album" varStatus="status">
<tr>
<td class="hidden-xs-portrait">${album.aid}</td>
<td><img src="${filePath}${album.pic}" /></td>
<td> ${album.aname} </td>
<td class="hidden-xs-portrait">${album.company}</td>
<td class="hidden-xs"> <p><strong><f:formatDate value="${album.pdate}" pattern="yyyy-MM-dd"></f:formatDate></strong></p> </td>
<td class="hidden-xs">${album.lang}</td>
<td><button class="btn btn-sm btn-primary" type="button" modify aid="${album.aid}"> 修改 </button>
<button data-toggle="button" class="btn btn-sm btn-warning" aid="${album.aid}"> 删除 </button></td>
</tr>
</c:forEach>
展现图片时的地址采用的时服务器地址加上相对地址,这样就可以在文件服务器中找到对应的图片。
因此想要在该界面加载出图片就必须先启动文件服务器
后台的controller:
/**
* 查询专辑信息
* 分页 条件 查询
* 专辑名称 查看的页码 每页展示数量
* @return
*/
@RequestMapping("/list")
public String list(AlbumQuery mq, Model model){
//1.程序严谨性:判断前端传递的参数
//有没有传递想看第几页,没有设计为访问第一页
if(mq.getPageNo()==null){
mq.setPageNo(1);
}
//2.调用业务层进行分页条件查询
Page<Album> page = albumService.selectByPage(mq);
//3.返回前端想要的数据 返回一个页对象
model.addAttribute("page",page);
//要进行搜素参数的回显功能
model.addAttribute("mq",mq);
return "album";
}
最后界面呈现结果如下:
专辑添加
本次添加我们依然采用弹出窗口,并进行表单提交
弹出窗口如下:
<div id="albumPop" style="margin-right: 50px;margin-top: 50px; display: none">
<form id="addAlbumForm" class="layui-form" method="post" action="/album/addAlbum" enctype="multipart/form-data" lay-filter="example">
<div class="layui-form-item" >
<label class="layui-form-label" style="width:100px">专辑名字</label>
<div class="layui-input-block">
<input type="text" name="aname" style="color: black; border-color: lightgray;background-color: white" lay-verify="aname" autocomplete="off" placeholder="请输入专辑名" class="layui-input">
</div>
</div>
<div class="layui-form-item" >
<label class="layui-form-label" style="width:100px">发行公司</label>
<div class="layui-input-block">
<input type="text" name="company" style="color: black; border-color: lightgray;background-color: white" lay-verify="company" autocomplete="off" placeholder="请输入公司名" class="layui-input">
</div>
</div>
<div class="layui-form-item" >
<label class="layui-form-label" style="width:100px">发行时间</label>
<div class="layui-input-block">
<input type="text" id="addPdate" name="pdate" style="color: black; border-color: lightgray;background-color: white" lay-verify="pdate" autocomplete="off" placeholder="请输入发行时间" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="width:100px">图片</label>
<div class="controls form-group">
<div class="col-sm-4 col-md-2">
<div class="image-row">
<div class="image-set">
<a class="example-image-link" id="albumImg1" href="../../img/gallery-photo/image-3.jpg" data-lightbox="example-set" title="Click on the right side of the image to move forward.">
<img id="albumImg" class="example-image" src="../../img/gallery-photo/thumb-3.jpg" alt="Plants: image 1 0f 4 thumb" width="150" height="150" />
</a>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label for="i-file" class="layui-form-label" style="width:100px">选择文件</label>
<!--<div class="col-sm-4 control-label">选择文件</div>-->
<div id="examples" class="section examples-section">
<div class="col-sm-6">
<div class="input-group">
<input id='location' class="form-control" onclick="$('#i-file').click();">
<label class="input-group-btn">
<input type="button" id="i-check" value="选择封面" class="btn btn-primary" onclick="$('#i-file').click();">
</label>
</div>
</div>
<input type="hidden" id="pic" name="pic" lay-verify="pic">
<input type="hidden" id="lastImg" name="lastImg">
<input type="file" name="picfile" id='i-file' accept=".jpg, .png" onchange="submitFile()" style="border-color: lightgray;background-color: lightgray;display: none">
</div>
</div>
<div class="layui-form-item" >
<label for="lang1" class="layui-form-label " style="width:100px">语种</label>
<div class="layui-input-block">
<input id="lang1" type="text" name="lang" style="color: black; border-color: lightgray;background-color: white" lay-verify="lang" autocomplete="off" placeholder="请输入语种" class="layui-input">
</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>
弹出效果如下:
在界面中选择图片完成后,选择的文件会显示在界面中,实现该效果使用的方法为定义了提交方法,在提交方法中会将文件上传到服务器,随后返回文件的绝对地址和相对地址,并将绝对地址值体现在img标签中,从而实现了图片在页面中的展示。相对地址放在表单中的隐藏域中用于提交表单时实现添加。具体方法如下:
function submitFile(){
$("#location").val($("#i-file").val());
$("#addAlbumForm").ajaxSubmit({
url:"/upload/uploadFile",
data:{
fileType:"pic"
},
dataType:"json",
success:function (json) {
$("#albumImg").attr("src",json.realPath);
$("#albumImg1").attr("href",json.realPath);
$("#lastImg").val(json.realPath);
$("#pic").val(json.relativePath);
}
})
}
之后就和流派添加时一样,点击添加按钮时提交表单,实现专辑的添加,添加后重新将专辑页面的表单提交进行页面的刷新。这里不再详细解释并展示代码,和流派添加时不同的地方只是参数不同而已。
专辑删除与修改
删除功能和修改功能和流派模块中也是一样的,删除方法完全相同,修改方法有微小的区别,下面进行简单的解释。
修改界面时要将选中的信息进行回显,包括图片和图片地址,目标效果如下:
另外弹出层与添加弹出层同样相同,只是修改参数,将两个弹出层区别开来。
<div id="albumPop1" style="margin-right: 50px;margin-top: 50px; display: none">
<form id="updateAlbumForm" class="layui-form" method="post" action="/album/addAlbum" enctype="multipart/form-data" lay-filter="example">
<input type="hidden" name="aid" id="aid">
<div class="layui-form-item" >
<label class="layui-form-label" style="width:100px">专辑名字</label>
<div class="layui-input-block">
<input id="atname" type="text" name="aname" style="color: black; border-color: lightgray;background-color: white" lay-verify="aname" autocomplete="off" placeholder="请输入专辑名" class="layui-input">
</div>
</div>
<div class="layui-form-item" >
<label class="layui-form-label" style="width:100px">发行公司</label>
<div class="layui-input-block">
<input id="acompany" type="text" name="company" style="color: black; border-color: lightgray;background-color: white" lay-verify="company" autocomplete="off" placeholder="请输入公司名" class="layui-input">
</div>
</div>
<div class="layui-form-item" >
<label class="layui-form-label" style="width:100px">发行时间</label>
<div class="layui-input-block">
<input id="adate" type="text" id="updatePdate" name="pdate" style="color: black; border-color: lightgray;background-color: white" lay-verify="pdate" autocomplete="off" placeholder="请输入发行时间" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" style="width:100px">图片</label>
<div class="controls form-group">
<div class="col-sm-4 col-md-2">
<div class="image-row">
<div class="image-set">
<a class="example-image-link" id="albumImg3" href="../../img/gallery-photo/image-3.jpg" data-lightbox="example-set" title="Click on the right side of the image to move forward.">
<img id="albumImg2" class="example-image" src="../../img/gallery-photo/thumb-3.jpg" alt="Plants: image 1 0f 4 thumb" width="150" height="150" />
</a>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label for="i-file1" class="layui-form-label" style="width:100px">选择文件</label>
<!--<div class="col-sm-4 control-label">选择文件</div>-->
<div id="examples1" class="section examples-section">
<div class="col-sm-6">
<div class="input-group">
<input id='location1' class="form-control" onclick="$('#i-file1').click();">
<label class="input-group-btn">
<input type="button" id="i-check1" value="选择封面" class="btn btn-primary" onclick="$('#i-file1').click();">
</label>
</div>
</div>
<input type="hidden" id="pic1" name="pic" lay-verify="pic">
<input type="hidden" id="lastImg1" name="lastImg1">
<input type="file" name="picfile" id='i-file1' accept=".jpg, .png" onchange="submitFile1()" style="border-color: lightgray;background-color: lightgray;display: none">
</div>
</div>
<div class="layui-form-item" >
<label for="lang1" class="layui-form-label " style="width:100px">语种</label>
<div class="layui-input-block">
<input id="lang2" type="text" name="lang" style="color: black; border-color: lightgray;background-color: white" lay-verify="lang" autocomplete="off" placeholder="请输入语种" class="layui-input">
</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>
回显时与流派修改时一样,也是根据id查询到信息,并传递到页面上,
回显的方法:
/**
* 流派修改的回显数据方法
*/
@ResponseBody
@PostMapping("/getAlbum")
public Album getAlbum(int aid){
//借助于第三方工具类
Album album=albumService.selectByPrimaryKey(aid);
return album;
}
前端js:
var pop1;
$("[modify]").click(function () {
var aid = $(this).attr("aid");
$.ajax({
url: "/album/getAlbum",
type: "post",
data: {
aid:aid
},
dataType: "json",
success: function (jsonObj) {
$("#aid").val(jsonObj.aid);
$("#location1").val(jsonObj.pic);
$("#atname").val(jsonObj.aname);
$("#acompany").val(jsonObj.company);
$("#adate").val(jsonObj.pdate);
$("#lang2").val(jsonObj.lang);
$("#albumImg2").attr("src","http://localhost:8083"+jsonObj.pic);
$("#albumImg3").attr("href","http://localhost:8083"+jsonObj.pic);
$("#pic1").val(jsonObj.pic);
}
})
pop1 = layer.open({
type: 1,
area: [900,650],
content: $('#albumPop1')
});
})
随后点击修改按钮完成修改。
**
歌手管理
歌手查询与删除
该模块的分页查询与删除与前两模块的基本相同,该模块和其他模块的区别主要体现在添加与修改模块。
查询时唯一与前面不同的地方是要将全部的流派查询出来,体现在界面中的下拉框中
下面为查询展示的Controller方法
@Autowired
private SongerService songerService;
@Autowired
private MtypeService mtypeService;
@RequestMapping("/list")
public String list(SongerQuery mq, Model model){
//1.程序严谨性:判断前端传递的参数
//有没有传递想看第几页,没有设计为访问第一页
if(mq.getPageNo()==null){
mq.setPageNo(1);
}
//2.调用业务层进行分页条件查询
Page<Songer> page = songerService.selectByPage(mq);
//为了前端能显示所有流派做查询
List<Mtype> mtypeList = mtypeService.selectAll();
//3.返回前端想要的数据 返回一个页对象
model.addAttribute("page",page);
//要进行搜素参数的回显功能
model.addAttribute("mq",mq);
model.addAttribute("mtypes",mtypeList);
return "songer";
}
前端代码接收
<label for="tid" class="control-label">流派</label>
<div class="controls form-group">
<select id="tid" name="tid" class="form-control ">
<option value="">--请选择--</option>
<c:forEach items="${mtypes}" var="mtype">
<option value="${mtype.tid}" <c:if test="${mtype.tid == mq.tid}">selected</c:if>>${mtype.tname}</option>
</c:forEach>
</select>
</div>
最后页面完成后效果如下:
歌手添加与修改
这里采用新界面来进行添加后修改。
在添加时与前面模块不同的是要先查出所有的流派并体现在下拉框中,其余完全相同。查出所有的流派并体现在下拉框在上面也有说明,所以也不再多做解释。
添加最终效果界面如下:
实现修改功能在进行回显时要将要修改的信息体现在界面中,其他框中回显与前面相同,不过在进行下拉框选项回显时需要使用另一种方法。在前端界面中的下拉框中需要做一些逻辑处理,处理如下:
<div class="control-group">
<label for="tid" class="control-label">流派<span class="required">*</span></label>
<div class="controls form-group">
<div data-toggle="buttons" class="btn-group col-sm-2 " >
<select id="tid" name="tid" class="form-control ">
<c:forEach items="${mtypes}" var="mtype">
<option value="${mtype.tid}" ${mtype.tid==songer.tid?"selected='selected'" :""} >${mtype.tname}</option>
</c:forEach>
</select>
</div>
</div>
</div>
在该下拉框中会用所有的流派id和该被修改信息中的流派id进行对比,如果相同则选中并将流派名体现出来,下面的是否热歌选项一样,用查到的信息进行对比判断选中
<div class="control-group">
<label for="isHot" class="control-label">是否热歌<span class="required">*</span></label>
<div class="controls form-group">
<div data-toggle="buttons" class="btn-group col-sm-2" >
<select id="isHot" name="isHot" class="form-control ">
<option value="1" ${"1"==song.isHot?"selected='selected'" :""} >是</option>
<option value="0" ${"0"==song.isHot?"selected='selected'" :""} >否</option>
</select>
</div>
</div>
</div>
随后点击修改按钮后即可完成修改,上面说过,这里修改和回显与前面基本相同,只是参数不同,因此不再多做解释。
歌曲管理
该模块用到的技术在前面三个模块中都已经有体现,不明白可以看上面部分和上一篇文章,所以也不多做展示,只展现完成效果。
界面展示:
添加页面:这里有一点小区别,这里需要上传mp3文件。
至此,后台模块的全部功能全部实现,下面开始进行前台的开发
前台模块基本配置
前台界面主要进行信息的展示,不涉及到信息的修改,该项目中前台主要实现了对歌曲和歌手的查看,并可以将歌曲正常播放(前提是后台已经添加了mp3文件)
前台模块也是mavenWeb项目,也就是hami_portal子模块,第一篇文章中有提到。
首先在pom.xml中进行依赖的导入:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hami_parent</artifactId>
<groupId>com.qcby</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<!--前台有界面 用户访问 war包-->
<artifactId>hami_portal</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.qcby</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>hami_core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
</dependencies>
</project>
导入前端界面和静态资源,引入完结构如下:
随后在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"/>
<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>
<mvc:default-servlet-handler/>
</beans>
再配置webapp下的web.xml
<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">
<filter>
<filter-name>SpringCharacterEncodingFilter</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>SpringCharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>
</web-app>
前台歌曲查看
想要在查看所有的歌曲,首先在后端写查询方法,在体现在界面上。
该界面中可以根据流派,是否热歌和是否新歌进行条件查询,查到的歌曲以每五条为一组进行展示,想要查看更多可以查看更多再查看五条,知道没有歌曲为止。最终效果如下:
所以创建SongController并编写查询方法
@RequestMapping("/dofindAll")
public String doFindAll(SongQuery mq, Model model){
//1.程序严谨性:判断前端传递的参数
//有没有传递想看第几页,没有设计为访问第一页
if(mq.getPageNo()==null){
mq.setPageNo(1);
}
//2.调用业务层进行分页条件查询
Page<Song> page = songService.selectByPage(mq);
List<Mtype> mtypelist = mtypeService.selectAll();
//3.返回前端想要的数据 返回一个页对象
model.addAttribute("page",page);
model.addAttribute("mtypes",mtypelist);
model.addAttribute("mq",mq);
return "search";
}
实现条件查询的方法为在前端界面中选中后会将选中的参数传递到后端重新进行查询刷新界面同时记录在界面中的隐藏域中。刷新后再提取隐藏域中的信息进行回显,来实现已经选中的视觉效果。
前端方法如下:
var tid = "";
var isHot = "";
var isNew = "";
$(function () {
//指定点击事件
(".filter p a").click(function () {
//移除同辈的a链接的样式
$(this).siblings().removeClass("current")
//把点击的a链接的样式加上
$(this).addClass("current");
//获得流派的选中值
var tid = $("a[ftype='mtype'][class='current']").attr("value");
var isHot = $("a[ftype='isHot'][class='current']").attr("value");
var isNew = $("a[ftype='isNew'][class='current']").attr("value");
//alert(tid+" "+isHot+" "+isNew);
window.location.href = "/song/dofindAll?tid=" + tid + "&isHot=" + isHot + "&isNew=" + isNew;
})
tid = $("#tid").val();
isHot = $("#isHot").val();
isNew = $("#isNew").val();
//流派的回显
$("a[ftype='mtype'][class='current']").removeClass("current");
$("a[ftype='mtype'][value='" + tid + "']").addClass("current");
//热门回显
$("a[ftype='isHot'][class='current']").removeClass("current");
$("a[ftype='isHot'][value='" + isHot + "']").addClass("current");
//新歌回显
$("a[ftype='isNew'][class='current']").removeClass("current");
$("a[ftype='isNew'][value='" + isNew + "']").addClass("current");
})
想要实现查看更多效果的方法为点击后修改page中的pagesize并重新刷新界面来达到查询到更多的信息。前端JS中实现如下:
function loadMore() {
//1
var pageNoPortal = parseInt($("#pageNoPortal").val());
//计算pageSize
var pageSize = 5 * (++pageNoPortal);
window.location.href = "/song/dofindAll?tid=" + tid + "&isHot=" + isHot + "&isNew=" + isNew + "&pageSize=" + pageSize + "&pageNoPortal=" + pageNoPortal;
}
该界面中点击具体的一首歌曲可以将该歌曲进行播放,也可以同时选中多个歌曲加入播放列表进行顺序播放。最终界面实现如下:
前端处理时将选中的歌曲的id集合传递给后端并跳转到播放界面
代码如下:
function playsongs() {
var songs = $("tbody tr td input[type='checkbox']:checked")
var sids = ""; //"1,2,3,4,5,"
songs.each(function () {
var sid = $(this).val();
sids = sids + sid+","
})
window.open("/song/play?sids="+sids,"play");
}
function play(sid) {
window.open("/song/play?sids="+sid,"play");
}
这里后端的逻辑较为复杂,编写播放方法如下:
/**实现歌曲列表
*
* @param sids
* @param model
* @param response
* @param request
* @return
*/
@RequestMapping("/play")
public String play(String sids, Model model, HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {
//前端传递的是sids数组
//定义一个数组进行装载 {"2","3","4"}
String[] idsArr = null;
//判断ids不能为null
if(sids !=null && !"".equals(sids)){
idsArr = sids.split(",");
}
//同时解读上次播放列表有没有歌曲
Cookie[] cookies = request.getCookies();
String cookieIds = null;
//存上次的sid值 已有的播放列表的值
String[] idsArrCookie = null;
//遍历浏览器中的所有cookie 找到自己的
if(cookies !=null){
for(Cookie cookie:cookies){
String cookiename = cookie.getName();
if("playids".equals(cookiename)){
//获取当前播放列表中的值
cookieIds = cookie.getValue();
//解码
cookieIds =URLDecoder.decode(cookieIds,"UTF-8");
}
}
}
//拿到cookie的值后判断是否为空 不为空 解析 拿到对应的id
if(cookieIds !=null){
idsArrCookie=cookieIds.split(",");
}
//创建集合 装int类型 Integer类型 为了传递数据库进行id查询
List<Integer> list = new ArrayList<Integer>();
//定义往前端返回的cookie
cookieIds = "";
//判断id的数组不为null
if(idsArr!=null){
//将数组中的字符串id值转变成integer的值放入到集合当中去 为了后面查询使用
for (String s : idsArr) {
//利用包装类的构造类
list.add(new Integer(s));
cookieIds=cookieIds+s+",";
}
if(idsArrCookie !=null && !"".equals(idsArrCookie)){
for (String s : idsArrCookie) {
Integer sid = new Integer(s);
boolean exists = false;
for (Integer i : list) {
if(sid.equals(i)){
exists=true;
break;
}
}
if(!exists){
list.add(sid);
cookieIds=cookieIds+s+",";
}
}
}
}
//查询返回
//通过集合中sid的值查询所有要添加到播放列表的歌曲
List<Song> songs= songService.selectSongerBySids(list);
cookieIds = URLEncoder.encode(cookieIds,"UTF-8");
Cookie cookie = new Cookie("playids", cookieIds);
cookie.setMaxAge(60*60*24*30);
cookie.setPath("/");
response.addCookie(cookie);
model.addAttribute("songs",songs);
return "player";
}
@ResponseBody
@RequestMapping("/getSong")
public Song getSong(Integer sid){
Song song = songService.getSong(sid);
return song;
}
至此,实现了前台歌曲的查看与播放
前台歌手查看
前台在展示歌手时也可以进行条件查询,根据歌手的流派和是否热门进行查看。并在展示时每五个歌手为一组占据一行的空间。最终实现效果如下:
条件查询与歌曲的条件查询相同,因此不再多做说明,下面主要说明如何实现一行呈现五个歌手,这是该界面的一个难点。
想要实现这个效果主要使用的方法是将查询到歌手每五个装进一个小集合,再将所有小集合装进一个大集合中,在前端界面中通过嵌套c:foreach来将结果呈现出来,前端界面代码如下:
<div class="albums" data-spm="1392350021">
<c:forEach items="${sList}" var="subList">
<div class="album_list">
<c:forEach items="${subList}" var="songer">
<div class="album" data-needpay="0" data-playstatus="1" data-downloadstatus="1">
<div class="image">
<a target="_blank" title="${songer.srname}" href="/songer/getSonger?srId=${songer.srid}">
<img src="${filePath}${songer.pic}" alt="${songer.srname}">
<b class="icon toplay" onclick="playalbum(406532);return false;" style="display: none;"></b>
<dl style="display: none;">
<dt>
<b class="icon toheart"></b>
<b class="icon todropmenu"></b>
</dt>
<dd style="display: none;">
<ul>
<li onclick="tag(406532,5);return false;"><b class="icon tofavourite"></b>收藏</li>
<li onclick="album2collect(406532);return false;"><b class="icon tocollect"></b>添加到</li>
<li onclick="recommend(406532,33);return false;"><b class="icon toshare"></b>分享到</li>
</ul>
</dd>
</dl>
<sup title="${songer.srname}"></sup> </a>
</div>
<div class="info">
<p>
<a target="_blank" title="${songer.srname}" href="#">${songer.srname}</a>
</p>
</div>
</div>
</c:forEach>
</div>
</c:forEach>
</div>
后端中查询方法如下:
@RequestMapping("/dofindAll")
public String doFindAll(SongerQuery mq, Model model){
//1.程序严谨性:判断前端传递的参数
//有没有传递想看第几页,没有设计为访问第一页
if(mq.getPageNo()==null){
mq.setPageNo(1);
}
mq.setPageSize(20);
Page<Songer> page = songerService.selectByPage(mq);
List<Songer> list = page.getList();
List<List<Songer>> sList = new ArrayList<>();
List<Songer> subList =null;
//循环将小集合装入大集合
for(int i=0;i<list.size();i++){
if(i%5 == 0){
subList = new ArrayList<>();
sList.add(subList);
}
Songer songer = list.get(i);
subList.add(songer);
}
List<Mtype> mtypelist = mtypeService.selectAll();
//3.返回前端想要的数据 返回一个页对象
//model.addAttribute("subList",subList);
model.addAttribute("sList",sList);
model.addAttribute("mtypes",mtypelist);
model.addAttribute("mq",mq);
return "songers";
}
这样前台歌手的整体展示完毕,下面还有最后一个功能。点击某个具体歌手后可以查看他的个人信息,这里就是简单的查询然后展现在界面上,没有难点,所有下面只展示最终界面不再展示代码。
至此,该项目全部模块完成,我用来三篇文章来完成自己对该项目的一个梳理解析。