前端为 LayUI 框架,所涉及到的知识点包括 图片预览,文件上传,blob数据处理 ,LayUI数据表格加载时自动选中 和数据加密(DEC)。
1.轮播效果的实现
html代码,定义轮播容器
<div class="layui-carousel" id="rotation">
<div carousel-item id="rotationItem" style="height: 350px">div>
div>
JS代码
jQuery处理blob数据的方法为 function getBinary()
/**
* Created by suzhaoxi on 2021/12/13.
*/
layui.use(['table','jquery','HussarAjax','carousel','layer','Hussar','element'], function() {
var element = layui.element;
var table = layui.table;
var Hussar = layui.Hussar;
var $ = layui.jquery;
var layer = layui.layer;
const ctxPath = Hussar.ctxPath;
var $ax = layui.HussarAjax;
var carousel = layui.carousel;
// 公用方法,jQuery处理图片流,用js写
function getBinary(url, args, id ) {
var xmlhttp = new XMLHttpRequest();
var data = eval(args);
var i = 0;
for (var key in data) {
if (i++ === 0) {
url += '?' + key + "=" + data[key];
} else {
url += '&' + key + "=" + data[key];
}
}
xmlhttp.open("GET", url, true);
xmlhttp.responseType = "blob";
xmlhttp.onload = function () {
var img = $('#'+id);
window.URL.revokeObjectURL(img.src);
$('#'+id).attr('src', window.URL.createObjectURL(this.response));
};
xmlhttp.send();
}
// 生成轮播的方法
function rotation() {
$.ajax({
// 从数据库中得到一些基本参数
url: ctxPath + "/homeRotation/getRotationListsInHome" ,
type: 'POST',
dataType: 'JSON',
data: {},
success: function (result) {
var data = result.data;
console.log(result)
if (result.count == 0){ // 如果没有图片,就放一张默认的图片
$("#rotationItem").append('
');
}else {
for (let i = 0; i < result.count; i++) {
var pic = data[i].pic;
var url = data[i].url;
var id = data[i].id;
var title = data[i].title;
// 对轮播的 div 进行一些设置,包括 id , 标题 , href 和 图片(该处只得到一个图片的路径,需进一步处理)
$("#rotationItem").append('+ ctxPath + url + '" target="_blank">
+ id + '" title=" '+title+' " style="auto;height: 100%;width: 100%"> ');
function readImg() {
// 处理图片(根据图片路径pic找到图片,转化成二进制流数据并展示)
// homeRotation/IoReadImage,读取本地文件的方法,下文会写
getBinary("/homeRotation/IoReadImage",{'src': pic},id);
}
readImg();
}
}
// 重新加载轮播(二者缺一不可)
var ins = carousel.render({
elem: '#rotation' // 绑定容器ID
,interval: 4500 // 设置切换时间
,height: '350px' // 设置容器高度
,width: '100%' //设置容器宽度
,arrow: 'always' //始终显示箭头;
});
ins.reload({
elem: '#rotation' // 绑定容器ID
,interval: 4500 // 设置切换时间
,height: '350px' // 设置容器高度
,arrow: 'always' // 始终显示箭头
,anim: 'always' // 换动画方式,可选fade,default,updown
});
}
});
}
rotation();
// 这里定义一个轮播容器
carousel.render({
elem: '#rotation' // 绑定容器ID
,interval: 4500 // 设置切换时间
,height: '350px' // 设置容器高度
,width: '100%' //设置容器宽度
,arrow: 'always' //始终显示箭头
});
});
所以轮播实现至少要包含两个基本元素,图片上传和链接的跳转。上传图片时,就必然牵涉到预览。
2.轮播后台配置页
因为一条轮播,不仅是图片上传,还包括绑定公示公告等( 链接是开发的项目中的一些公示公告,均为 get 请求跳转),所以,一条轮播主要的两个按钮为 图片上传 和 确定保存( 或修改)。
html代码
<form class="layui-form">
<div class="tec-empty">div>
<div class="theRealContent">
<div class="layui-form-item" id="previewImg">
<div class="layui-upload-list">
<img class="layui-upload-img" id="demo1" src="">
<p id="demoText">p><br>
<span style="color: #27b7b0">-- 效果图预览 -- 推荐比例 (4:1) --span>
div>
div>
<div class="layui-form-item">
<div class="layui-col-md12">
<label class="layui-form-label tec-label-four">图片上传:label>
<button type="button" class="layui-btn four floatLeft" id="rotationUpload">上传button>
div>
div>
<div class="layui-layer-btn layui-layer-btn- btnFixed layerBtnCenter btn-box btn-box-1">
<a class="layui-layer-btn0" id="back">取消a>
<a class="layui-layer-btn1" id="updateRotation" >确定a>
<button class="layui-btn" type="button" style="display: none" id="uploadRotation" >real确定button>
div>
form>
JS代码
有必要说明一下,这里的逻辑。首先 upload.render 绑定的 rotationUpload 上传按钮,将图片加载到前端,预览方法为 obj.preview ,但此时并没有 触发上传方法homeRotation/uploadImg, 真正触发上传方法的是 uploadRotation 按钮。所以在点击 updateRotation 确定(即保存或修改时),才将预览的图片上传到指定位置 。然后上传图片和保存轮播数据 是两个方法,即上传图片ajax成功之后的回调函数再发送 保存数据的ajax。其他的就是 保存 有三种状态有轮 只修改图片, 只修改数据, 既修改图片又修改数据,判断好就行了。
//上传图片
var uploadInst = upload.render({
elem: '#rotationUpload' // 图片上传按钮
,url: Hussar.ctxPath+ '/homeRotation/uploadImg'
,auto: false // 不自动上传
,multiple: false // 是否允许上传多张图片
,accept: 'file'
// ,exts: 'png|jpg|bmp|jfif' //设置只能上传图片格式文件
,size: 50000 //最大允许上传的文件大小
,bindAction: '#uploadRotation' // 绑定上传按钮
,choose: function (obj) {
//预读本地文件示例,不支持ie8
obj.preview(function (index, file, result) {
$('#imgName').val(file.name); //图片名字
imageName = file.name;
$('#demo1').attr('src', result); //图片链接(base64)
});
}
, done: function (res) { // 成功之后的回调函数
if (res.code === 200){
$("#realPath").val(res.realPath);
var ajax = new $ax(Hussar.ctxPath + "/homeRotation/preserveRotation",
function (data) {
if (data.code === 200){
Hussar.success(data.message);
jumpToListPage(); // 回到列表页面
}else {
Hussar.error(data.message);
}
},function (data) {
Hussar.error('提交失败!' + data.message + '!');
});
ajax.setData(JSON.stringify(
{
pic: $("#realPath").val(),
title: $("#title").val(),
noticeName: $("#noticeName").val(),
noticeId: $("#noticeId").val(),
noticeUrl: $("#noticeUrl").val(),
noticeType: $("#noticeType").val(),
page: $("#page").val()
}));
}
ajax.setContentType('application/json');
ajax.start();
}else{
return Hussar.error(res.message);
}
}
, error: function () { //失败之后的回调函数,并实现重传
var demoText = $('#demoText');
demoText.html('上传失败 重试');
demoText.find('.demo-reload').on('click', function () {
uploadInst.upload();
});
}
})
// 新增轮播 保存按钮
$("#addRotation").click(function () {
// 数据校验的方法,根据需要来
if (rotationValidation()&&homeRotationConfigureHandling.check()){
var operation = function () {
$("#noticeUrl").val(homeRotationConfigureHandling.seItem.noticeUrl),
$("#noticeType").val(homeRotationConfigureHandling.seItem.noticeType),
$("#noticeId").val(homeRotationConfigureHandling.seItem.proId),
$("#title").val(homeRotationConfigureHandling.seItem.pName),
$("#uploadRotation").click(); // 就是这里,触发图片上传
};
Hussar.confirm('确定要保存吗?',operation);
}
})
// 回到列表页面
function jumpToListPage() {
setTimeout(function () {
if (window.parent == window) {
window.location.href = window.location;
} else {
var ifrmes = window.parent.document.getElementsByTagName("iframe");
for (var i = 0; i < ifrmes.length; i++) {
var iframeObj = ifrmes[i];
if (iframeObj.src.indexOf('/homeRotation/toHomeRotationIndex') != -1) {
iframeObj.src = iframeObj.src;
window.parent.HussarTab.tabChange(iframeObj.getAttribute("tab-id"))
break;
}
}
window.parent.HussarTab.tabDelete(window.frameElement.getAttribute("tab-id"));
}
}, 2000);
}
Controller层代码
/**
* 上传图片
* @param multipartFile
* @return
* @throws Exception
*/
@RequestMapping("/homeRotation/uploadImg")
@ResponseBody
public JSONObject uploadImg(@RequestParam("file") MultipartFile multipartFile) throws Exception {
return iHomeRotationService.uploadRotationImg(multipartFile);
}
/**
* 新增一条 轮播数据
* @param rotationInfoVO
* @return
*/
@RequestMapping("/preserveRotation")
@ResponseBody
public Tip preservationRotation(@RequestBody RotationInfoVO rotationInfoVO){
return iHomeRotationService.insertRotation(rotationInfoVO);
}
Service层代码
链接的全拼包含 敏感字段( noticeId),需要加密起来,其方法为
encrypt = DesUtil.encrypt(RotationNoticeUtil.ROTATION_DEC_PWD, rotationInfoVO.getNoticeId());
/**
* 上传轮播图片,其核心就是 multipartFile.transferTo(newFile);
* @param multipartFile
* @return
*/
@Override
public JSONObject uploadRotationImg(MultipartFile multipartFile) throws Exception {
JSONObject json = new JSONObject();
if (!StringUtils.isEmpty(String.valueOf(multipartFile)) && multipartFile.getSize() > 0) {
String id = UUID.randomUUID().toString().replaceAll("-", ""); // 随机生成id
String fileName = multipartFile.getOriginalFilename(); // 获取上传的文件的文件名
String suffix = fileName.substring(fileName.lastIndexOf(".")); // 获取 文件的后缀 (例 .png 这种)
//检查文件名是否包含xss脚本
String xss = XssCheckerUtils.checkXss(fileName);
if (!org.apache.commons.lang3.StringUtils.isEmpty(xss)) {
throw new ApiException(HttpCode.BAD_REQUEST.value(), "invalid request , xss code:" + xss);
}
try {
// 系统上传的路径
String fileSavePath = this.hussarProperties.getFileUploadPath();
fileSavePath = fileSavePath.substring(0,fileSavePath.length()-1);
String realPath = fileSavePath + "/" +id + suffix; // 上传的文件全拼
File newFile = new File(realPath);
multipartFile.transferTo(newFile); // 上传的主方法
json.put("code",200);
json.put("message","上传成功!");
json.put("realPath",realPath);
json.put("id",id);
return json;
} catch (IOException e) {
throw new HussarException(BizExceptionEnum.UPLOAD_ERROR);
}
} else {
json.put("code",233);
json.put("message","文件为空!");
return json;
}
}
/**
* 新增一条 轮播数据,写这个主要是因为有个对noticeId的加密
* @param rotationInfoVO
* @return
*/
@Override
public Tip insertRotation(RotationInfoVO rotationInfoVO) {
String encrypt = "";
if (!rotationInfoVO.getNoticeId().isEmpty()){
// 对 noticeId 加密,得到密文 拼接到 url
encrypt = DesUtil.encrypt(RotationNoticeUtil.ROTATION_DEC_PWD, rotationInfoVO.getNoticeId());
}
rotationInfoVO.setId( UUID.randomUUID().toString().replaceAll("-", ""));
String url = "";
// 项目中所有的公示公告及法规等,跳转的href有三种情况,需做判断
if ((rotationInfoVO.getNoticeType() == null || rotationInfoVO.getNoticeType().isEmpty())
&& (rotationInfoVO.getNoticeId()== null || rotationInfoVO.getNoticeId().isEmpty())){
url = rotationInfoVO.getNoticeUrl();
}else if (rotationInfoVO.getNoticeType() == null || rotationInfoVO.getNoticeType().isEmpty()){
url = rotationInfoVO.getNoticeUrl() + "?pId="+encrypt;
}else {
url = rotationInfoVO.getNoticeUrl() + "?pId="+encrypt+"&type="+rotationInfoVO.getNoticeType();
}
rotationInfoVO.setUrl(url);
rotationInfoVO.setCreatTime(new Date(System.currentTimeMillis()));
rotationInfoVO.setCreatUser(ShiroKit.getUser().getName());
rotationInfoVO.setRotationStatus("0");
rotationInfoVO.setDel("0");
boolean b = false;
try {
b = iHomeRotationConfigureMapper.insertRotation(rotationInfoVO);
}catch (Exception e){
return new ErrorTip(HttpCode.INTERNAL_SERVER_ERROR.value(),"数据错误!");
}
if (b){
Tip successTip = new SuccessTip();
successTip.setMessage("添加成功!");
return successTip;
}else {
return new ErrorTip(HttpCode.CONFLICT.value(),"添加失败!");
}
}
3.LayUI数据表格加载时自动选中
当修改某条轮播时,之前绑定的数据需要在页面刚加载的时候就绑定上,所以这里有必要说一下自动选中的问题。主要方法 parseData:function
JS代码
table.render({
elem: '#uploadList'
,height:393
,url: ctxPath + ''
,method: 'POST'
,contentType : "application/json;charset=UTF-8"
,cols:[[
{type: 'checkbox', fixed: 'left'},
{title: '序号',type : 'numbers',align:"center" ,halign:'center',width: 48},
{title: '主键',width:'0%', field: 'proId',hide: true, align: 'center',halign:'center'},
{title: '标题', field:'pName', align: 'center',halign:'center'},
{title: '创建时间', field:'createTime', align: 'center',halign:'center',sort:true},
{title: '公告Url',width:'0%', field: 'noticeUrl',hide: true, align: 'center',halign:'center'},
{title: '公告类型',width:'0%', field: 'noticeType',hide: true, align: 'center',halign:'center'},
]]
,limit: 10
,page: true
,even: true
,where: {}
,done: function (res, curr, count) { //修改表头颜色
$('th').css({ 'background-color':'#a4dacd','color':'#000','font-size':'14px'});
$('th div').css('text-align', 'center');
}
,loading: true
// 数据格式解析的回调函数,选中的轮播的基本数据存在window全局变量中。
,parseData:function(res){
var data = res.data;
if (window.type == "update"){
for (let i = 0; i < data.length; i++) {
if ( window.noticeTitle == data[i].pName && window.noticeUrl == data[i].noticeUrl){
data[i]["LAY_CHECKED"] = true; // 设置 选中
}
}
}
return { //将重新处理好的数据传给表格以完成渲染
"code": res.code,
"count": res.count,
"data": data
};
}
});