OK,这一节讲解后台管理的最后一个功能:电影信息管理。主要对“添加电影信息”的列表管理。
我们先在 html 目录下,创建一个movieManage.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>电影管理页面</title>
<link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/default/easyui.css"></link>
<link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/icon.css"></link>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.min.js"></script>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.easyui.min.js"></script>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript">
//删除电影信息
function deleteMovie(){
var selectedRows=$("#dg").datagrid("getSelections");
if(selectedRows.length == 0){
$.messager.alert("系统提示","请选择要删除的数据!");
return;
}
var idArr = [];
for(var i = 0;i < selectedRows.length;i++){
idArr.push(selectedRows[i].id);
}
var ids = idArr.join(",");
$.messager.confirm("系统提示","您确定要删除这<font color=red>"+selectedRows.length+"</font>条数据吗?",function(r){
if(r){
$.post("/admin/movie/delete",{ids:ids},function(result){
if(result.flag){
$.messager.alert("系统提示","数据已成功删除!");
}else{
$.messager.alert("系统提示",result.errorMsg);
}
$("#dg").datagrid("reload");//重新加载网格数据
},"json");
}
});
}
//打开修改电影信息的 tab
function openMovieModifyTab(){
var selectedRows = $("#dg").datagrid("getSelections");
if(selectedRows.length != 1){
$.messager.alert("系统提示","请选择一个要修改的电影!");
return;
}
var row = selectedRows[0];
window.parent.openTab('修改电影信息','modifyMovie.html?id='+row.id,'icon-edit');
}
//格式化电影图片,src 的路径是一个虚拟的路径,WebConfig 类会映射本地磁盘的真实路径。前面有讲解过
function formatImageName(val,row){
return "<img width=100 height=100 src='/movieImages/"+val+"'>";
}
//格式化是否热门
function formatHot(val,row){
if(val == 1){
return "是";
}else{
return "否";
}
}
//查询电影信息
function searchMovie(){
$("#dg").datagrid('load',{
queryParams: {
'movieName': $("#nameId").val()
}
});
}
//格式化发布日期
function formatCreateTime(val,row) {
if (val != null) {
var date = new Date(val);
return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
}
}
</script>
</head>
<body style="margin: 1px">
<table id="dg" title="电影管理" class="easyui-datagrid"
fitColumns="true" pagination="true" rownumbers="true"
url="/admin/movie/list" fit="true" toolbar="#tb">
<thead>
<tr>
<th field="cb" checkbox="true" align="center"></th>
<th field="id" width="20" align="center">编号</th>
<th field="imageName" width="50" align="center" formatter="formatImageName">电影主图</th>
<th field="movieName" width="100" align="center">电影名称</th>
<th field="movieTitle" width="200" align="center">电影标题</th>
<th field="hot" width="30" align="center" formatter="formatHot">是否热门</th>
<th field="createTime" width="150" align="center" formatter="formatCreateTime">发布日期</th>
</tr>
</thead>
</table>
<div id="tb">
<div>
<a href="javascript:openMovieModifyTab()" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
<a href="javascript:deleteMovie()" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</div>
<div>
电影名称: <input type="text" id="nameId" size="20" onkeydown="if(event.keyCode==13) searchMovie()"/>
<a href="javascript:searchMovie()" class="easyui-linkbutton" iconCls="icon-search" plain="true">搜索</a>
</div>
</div>
</body>
</html>
接下来,我们开发 API 接口,用来查询电影信息的列表。
跟上一篇博客讲解的差不多,直接上代码,这部分功能写在 MovieInfoController即可:
//分页查询电影信息
@ResponseBody
@RequestMapping("/list")
public Map<String, Object> list(SearchInfo info, @RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "rows", required = false) Integer rows) {
Map<String, Object> map = movieInfoManage.getMovieList(info, page, rows);
return map;
}
MovieInfoManage接口增加代码:
//分页查询电影信息
Map<String, Object> getMovieList(SearchInfo info, int page, int rows);
MovieInfoManageImpl实现类增加代码:
//分页查询电影信息
@Override
public Map<String,Object> getMovieList(SearchInfo info, int page, int rows){
Map<String,Object> map = new HashMap<>();
try{
PageQuery pageQuery = SearchInfoUtil.getPageQuery(info,page,rows);
List<MovieInfoEntity> list = movieInfoService.findBy(pageQuery);
map = SearchInfoUtil.getResult(pageQuery,list);//封装结果
}catch (Exception e){
e.printStackTrace();
}
return map;
}
MovieInfoService类增加代码:
//分页查询
@Transactional(readOnly = true)
public List<MovieInfoEntity> findBy(PageQuery pageQuery){
return movieInfoDao.findBy(pageQuery);
}
MovieInfoDao接口增加代码:
//分页查询
List<MovieInfoEntity> findBy(PageQuery pageQuery);
MovieInfoEntityMapper.xml 文件增加代码(根据主键 id 倒序):
<!-- 分页查询 -->
<select id="findBy" resultMap="BaseResultMap">
select *
from movie_info
<where>
<if test=" movieName != null and movieName != '' ">
and movie_name like CONCAT('%',#{movieName,jdbcType=VARCHAR}, '%')
</if>
</where>
order by id desc
</select>
OK,重启服务,看下效果:
老铁们,没毛病!
接下来,测试查询功能,搜索框输入电影名称,支持模糊查询:
OK,搞定。
接下来,我们开发删除电影信息的 API 接口。在删除信息前,我们先弄一些测试数据,这里教一个方法:
insert into movie_info
(select null,movie_name,movie_title,hot,image_name,movie_content,create_time
from movie_info where id = 1);
多执行几次,就可以把电影信息表的 id=1 的信息新增到电影表中,做一些测试。
OK,开发删除接口,需要注意请求的 URL 路径,以及返回的 key。
MovieInfoController类增加代码:
//删除电影信息
@ResponseBody
@RequestMapping("/delete")
public Map<String,Object> delete(@RequestParam(value="ids")String ids){
Map<String, Object> map = new HashMap<>();
boolean flag = movieInfoManage.delete(ids);
map.put("flag", flag);
return map;
}
MovieInfoManage接口增加函数:
//删除电影信息
boolean delete(String ids);
MovieInfoManageImpl实现类增加代码:
//删除电影信息
@Override
public boolean delete(String ids) {
boolean flag = false;
try {
//用逗号分隔前端传递的 id 集合
String[] idsArr = ids.split(",");
for(String id : idsArr){
movieInfoService.delete(Integer.parseInt(id));
}
flag = true;
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
MovieInfoService增加代码:
//删除电影信息
@Transactional(readOnly = false)
public int delete(Integer id){
return movieInfoDao.delete(id);
}
MovieInfoDao增加代码:
//删除电影信息
int delete(Integer id);
MovieInfoEntityMapper.xml增加代码(之前已经有了无需增加):
<!-- 根据主键 id 删除 -->
<delete id="delete" parameterType="java.lang.Integer">
delete from movie_info
where id = #{id,jdbcType=INTEGER}
</delete>
OK,重启服务,测试:
OK,搞定!
还有一个功能,就是“修改”电影信息。
我们以一个新的 tab 页打开,把电影的主键 id 带过去。
OK,我们新建 modifyMovie.html 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>修改电影信息页面</title>
<link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/default/easyui.css"></link>
<link rel="stylesheet" type="text/css" href="/static/jquery-easyui-1.7.0/themes/icon.css"></link>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.min.js"></script>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/jquery.easyui.min.js"></script>
<script type="text/javascript" src="/static/jquery-easyui-1.7.0/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
</head>
<script type="text/javascript">
//提交修改
function submitData(){
$("#fm").form("submit",{
url:"/admin/movie/save", //仍然使用 save 方法,差别在于修改的时候有主键 id
onSubmit:function(){
var content= CKEDITOR.instances.content.getData()
if(content == ""){
$.messager.alert("系统提示","内容不能为空!");
return false;
}
return $(this).form("validate");
},
success:function(r){
var result = eval('('+r+')');
if(result.flag){
$.messager.alert("系统提示","保存成功!");
resetValue();
}else{
$.messager.alert("系统提示","保存失败!");
return;
}
}
});
}
//重置数据
function resetValue(){
$("#movieName").val("");
$("#movieTitle").val("");
$("#imageFile").val("");
$("#no").prop("checked", true);
CKEDITOR.instances.content.setData("");
}
</script>
</head>
<body style="margin: 10px">
<div id="p" class="easyui-panel" title="修改电影信息" style="padding: 10px">
<form id="fm" method="post" enctype="multipart/form-data">
<table cellspacing="20px">
<tr>
<td width="80px">电影名称:</td>
<td><input type="text" id="movieName" name="movieName" style="width: 400px;" class="easyui-validatebox" required="true"/></td>
</tr>
<tr>
<td width="80px">电影标题:</td>
<td><input type="text" id="movieTitle" name="movieTitle" style="width: 400px;" class="easyui-validatebox" required="true"/></td>
</tr>
<tr>
<td width="80px" valign="top">电影主图:</td>
<td>
<input type="hidden" id="imageName" name="imageName" />
<input type="file" id="imageFile" name="imageFile" />
</td>
</tr>
<tr>
<td width="80px">是否热门:</td>
<td><input type="radio" id="yes" value="1" name="hot" ><label for="yes">是 </label>
<input type="radio" id="no" value="0" checked="checked" name="hot" /><label for="no">否 </label></td>
</tr>
<tr>
<td valign="top">电影详情:</td>
<td>
<!-- 在引入 ckeditor.js 的文件里,textarea id="content" 会自动变成富文本编辑器 -->
<textarea id="content" name="movieContent" rows="20" cols="100"></textarea>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="hidden" id="id" name="id" />
<a href="javascript:submitData()" class="easyui-linkbutton" data-options="iconCls:'icon-submit'">发布信息</a>
</td>
</tr>
</table>
</form>
</div>
<script>
CKEDITOR.replace( 'content' );
//获取从上一个页面的路径传递的 id 值
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var url = decodeURI(decodeURI(window.location.search))
var r = url.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
var id = getQueryString("id");
console.log("id="+id);//打印到控制台
//根据电影信息 id 查询数据
$.post("/admin/movie/selectById",{id:id},function(result){
$("#id").val(result.id);
$("#movieName").val(result.movieName);
$("#movieTitle").val(result.movieTitle);
$("#imageName").val(result.imageName);
if(result.hot == 1){
$("#yes").prop("checked", true);
}else{
$("#no").prop("checked", true);
}
CKEDITOR.instances.content.setData(result.movieContent);//电影详情
},"json");
</script>
</body>
</html>
说明:
1、修改电影信息的请求 API 我们仍然使用 save 方法,不过需要做一点小变动:判断是否有主键 id,有则修改,无则新增。稍后说。
2、我们把电影主图也带出来了,只是不显示(hidden),而定义一个 type = "file" 的控件,重新上传。如果不选择文件,则主图不变。后台 API 有判断 file 是否是空的。
3、获取上一个页面传递的 id,以及调用 selectById 方法查询后台返回的电影信息,并设置到对应的位置中。
OK,我们看下后台 API 如何改造。
MovieInfoController类新增代码:
//根据 id 查询电影信息
@ResponseBody
@RequestMapping("/selectById")
public MovieInfoEntity selectById(@RequestParam(value="id")Integer id){
MovieInfoEntity entity = movieInfoManage.selectById(id);
return entity;
}
MovieInfoManage接口新增代码:
//根据 id 查询电影信息
MovieInfoEntity selectById(Integer id);
MovieInfoManageImpl实现类新增代码,同时修改 save 方法的判断逻辑:判断是否有主键 id。
//根据 id 查询电影信息
@Override
public MovieInfoEntity selectById(Integer id){
MovieInfoEntity infoEntity = null;
try{
infoEntity = movieInfoService.selectById(id);
//将获取到的电影信息进行解码,save 方法里有提到过
if(null != infoEntity){
//解码只需一个方法即可,解码后返回给前端,跟传递进来的时候是一样的。
String movieContent = HtmlUtils.htmlUnescape(infoEntity.getMovieContent());
infoEntity.setMovieContent(movieContent);//重新设置属性值
}
}catch (Exception e){
e.printStackTrace();
}
return infoEntity;
}
//新增电影信息
@Override
public boolean save(MovieInfoEntity entity, MultipartFile file) {
boolean flag = false;
try {
if (!file.isEmpty()) {
String originalFilename = file.getOriginalFilename();//获取文件名
String suffixName = originalFilename.substring(originalFilename.lastIndexOf("."));//获取文件后缀
String newFileName = System.currentTimeMillis() + suffixName;//新的文件名,避免文件名重复
FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(imageFilePath + newFileName));
entity.setImageName(newFileName);//设置到实体中
}
//将富文本的 html 代码转换特殊字符,防止对网站进行xss跨站攻击
//方案 1:转成十进制
String movieContent = HtmlUtils.htmlEscapeDecimal(entity.getMovieContent());
//方案 2:转成十六进制
//String movieContent = HtmlUtils.htmlEscapeHex(entity.getMovieContent());
entity.setMovieContent(movieContent);
int count = 0;
if(null != entity.getId()){//有主键 id,是修改
count = movieInfoService.update(entity);
}else{//没有主键id,是新增
count = movieInfoService.insert(entity);
}
if (count > 0) {
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
MovieInfoService新增代码:
//根据 id 查询电影信息
@Transactional(readOnly = true)
public MovieInfoEntity selectById(Integer id){
return movieInfoDao.selectById(id);
}
//更新电影信息
public int update(MovieInfoEntity entity){
return movieInfoDao.update(entity);
}
MovieInfoDao新增代码:
//根据 id 查询电影信息
MovieInfoEntity selectById(Integer id);
//更新电影信息
int update(MovieInfoEntity entity);
MovieInfoEntityMapper.xml新增代码(之前已经有的话可以不新增):
<!-- 更新 -->
<update id="update" parameterType="com.movie.entity.MovieInfoEntity">
update movie_info
<set>
<if test=" movieName != null and movieName != '' ">
movie_name = #{movieName,jdbcType=VARCHAR},
</if>
<if test=" movieTitle != null and movieTitle != '' ">
movie_title = #{movieTitle,jdbcType=VARCHAR},
</if>
<if test=" hot != null and hot != '' ">
hot = #{hot,jdbcType=CHAR},
</if>
<if test=" imageName != null and imageName != '' ">
image_name = #{imageName,jdbcType=VARCHAR},
</if>
<if test=" movieContent != null and movieContent != '' ">
movie_content = #{movieContent,jdbcType=LONGVARCHAR}
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<!-- 根据主键 id 查询 -->
<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select id, movie_name, movie_title, hot, image_name, movie_content,create_time
from movie_info
where id = #{id,jdbcType=INTEGER}
</select>
OK,我们做测试:
点击“修改”,打开新的 tab 页面
然后我们回到列表页面,点击分页下面的 刷新 按钮,即可查看到最新数据。
OK,到这里,我们的后台管理已经全部搞定。后面继续讲解用户端的使用页面。