Bootstrap 可视化HTML编辑器之summernote ,用其官网上的介绍就是“Super Simple WYSIWYG editor”,不过在我看来,与bootstrap中文官网上提供的“bootstrap-wysiwyg”要更simple,更漂亮,更好用!
虽然我之前尝试过使用bootstrap-wysiwyg,可参照Bootstrap wysiwyg富文本数据如何保存到mysql,但事后诸葛亮的经验告诉我,summernote绝对是更佳的富文本编辑器,这里对其工作team点三十二个赞!!!!!
经过一天时间的探索,对summernote有所掌握,那么为了更广大前端爱好者提供便利,我将费劲一番心血来介绍一下summernote,超级福利啊。
一、官方API和源码下载
工欲善其事必先利其器,首先把summernote的源码拿到以及对应官方API告诉大家是首个任务!
官网(demo和api) github源码下载,注意下载开发版
二、效果图
效果图1
效果图2
效果图3
三、开讲内容
大的方向为以下三个内容:
summernote的页面布局(资源引入、初始参数)
summernote从本地上传图片方法(前端onImageUpload方法、后端springMVC文件保存)
summernote所在form表单的数据提交
①、summernote的页面布局
<html lang ="zh-CN" >
<%@ include file="/components/common/taglib.jsp" %>
<head >
<meta charset ="UTF-8" >
<meta name ="viewport" content ="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title > summernote - bs3fa4title >
<script type ="text/javascript" src ="${ctx}/components/jquery/jquery.js" > script >
<link type ="text/css" rel ="stylesheet" href ="${ctx}/components/bootstrap/css/bootstrap.css" />
<script type ="text/javascript" src ="${ctx}/components/bootstrap/js/bootstrap.min.js" > script >
<link type ="text/css" rel ="stylesheet" href ="${ctx}/components/summernote/summernote.css" />
<script type ="text/javascript" src ="${ctx}/components/summernote/summernote.js" > script >
<script type ="text/javascript" src ="${ctx}/components/summernote/lang/summernote-zh-CN.js" > script >
<script type ="text/javascript" >
$('div.summernote' ).each(function () {
var $this = $(this );
var placeholder = $this .attr("placeholder" ) || '' ;
var url = $this .attr("action" ) || '' ;
$this .summernote({
lang : 'zh-CN' ,
placeholder : placeholder,
minHeight : 300 ,
dialogsFade : true ,
dialogsInBody : true ,
disableDragAndDrop : false ,
callbacks : {
onImageUpload : function (files) {
var $files = $(files);
$files.each(function () {
var file = this ;
var data = new FormData();
data.append("file" , file);
$.ajax({
data : data,
type : "POST" ,
url : url,
cache : false ,
contentType : false ,
processData : false ,
success : function (response) {
var json = YUNM.jsonEval(response);
YUNM.debug(json);
YUNM.ajaxDone(json);
if (json[YUNM.keys.statusCode] == YUNM.statusCode.ok) {
if (json[YUNM.keys.result]) {
var imageUrl = json[YUNM.keys.result].completeSavePath;
$this .summernote('insertImage' , imageUrl, function ($image) {
});
}
}
},
error : YUNM.ajaxError
});
});
}
}
});
});
script >
head >
<body >
<div class ="container" >
<form class ="form-horizontal required-validate" action ="#" enctype ="multipart/form-data" method ="post" onsubmit ="return iframeCallback(this, pageAjaxDone)" >
<div class ="form-group" >
<label for ="" class ="col-md-2 control-label" > 项目封面label >
<div class ="col-md-8 tl th" >
<input type ="file" name ="image" class ="projectfile" value ="${deal.image}" />
<p class ="help-block" > 支持jpg、jpeg、png、gif格式,大小不超过2.0Mp >
div >
div >
<div class ="form-group" >
<label for ="" class ="col-md-2 control-label" > 项目详情label >
<div class ="col-md-8" >
<div class ="summernote" name ="description" placeholder ="请对项目进行详细的描述,使更多的人了解你的" action ="${ctx}/file" > ${deal.description}div >
div >
div >
form >
div >
body >
html >
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
html5的标记是必须的 ,注意千万不能是
这种doctype,否则summernote的组件显示怪怪的,按钮的大小布局不一致,这里就不再上图了,但是千万注意!
bootstrap 的版本号最好为v3.3.5
1、布局div
<div class ="summernote" name ="description" placeholder="请对项目进行详细的描述,使更多的人了解你的" action="${ctx}/file" >${deal.description}div >
相信你也看到了我为div加上的三个属性name、placeholder、action,那么我们来详细介绍一下三个属性的作用:
name,为外层form表单提供summernote数据保存时的数据模型的属性名,和input标签的name属性作用一致,稍候在form提交的时候具体介绍。
placeholder,很直白,为summernote提供初始状态的文本描述,当然还需要后续加工,div显然是不支持placeholder属性的。
action,为图片上传提供后端接收地址,稍候在介绍图片上传onImageUpload会再次用到。
另外${deal.description}其实你不需要太多关注,和textarea的赋值的用法一致,就是单纯的显示保存后的内容。
2、summernote初始化
$('div.summernote' ).each(function () {
var $this = $(this );
var placeholder = $this .attr("placeholder" ) || '' ;
var url = $this .attr("action" ) || '' ;
$this .summernote({
lang : 'zh-CN' ,
placeholder : placeholder,
minHeight : 300 ,
dialogsFade : true ,
dialogsInBody : true ,
disableDragAndDrop : false ,
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
使用jQuery获取到页面上的summernote,对其进行初始化,我们来详细介绍列出参数的用法(先不介绍图片上传的onImageUpload 方法)。
lang ,指定语言为中文简体
placeholder ,summernote初始化显示的内容。
minHeight,最小高度为300,注意这里没有使用height, 是有原因的,这里稍作解释,就不上图了。当使用height指定高度后,假如上传比height高的图片,summernote就不会自动调整高度,并且前文中“效果图3”中标出的红色区域会不贴着图片,而溢出到summernote外部。
dialogsFade,增加summernote上弹出窗口滑进滑出的动态效果。
dialogsInBody,这个属性也很关键,默认为false,字面上的意思是summernote的弹出框是否在body中(in嘛) ,设置为false时,dialog的式样会继承其上一级外部(如上文中的form-horizontal)容器式样,那么显示的效果就很别扭,这里也不再上图;那么设置为true时,就不会继承上一级外部div的属性啦,从属于body嘛。
disableDragAndDrop,设置为false吧,有的时候拖拽会出点问题,你可实践。
②、summernote从本地上传图片方法
1、前端onImageUpload方法
假如问度娘如下的话:“onImageUpload方法怎么写?”,度娘大多会为你找到如下回答:
$(\'.summernote\').summernote({
height:300,
onImageUpload: function(files, editor, welEditable) {
sendFile(files[0],editor,welEditable);
}
});
});
function sendFile(file, editor, welEditable) {
data = new FormData();
data.append("file", file);
url = "http://localhost/spichlerz/uploads";
$.ajax({
data: data,
type: "POST",
url: url,
cache: false,
contentType: false,
processData: false,
success: function (url) {
editor.insertImage(welEditable, url);
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
以上资源来自于stackoverflow。
但其实呢,summernote-develop版本的summernote已经不支持这种onImageUpload写法,那么如今的写法是什么样子呢?参照summernote的官网例子。
onImageUpload
Override image upload handler(default: base64 dataURL on IMG tag). You can upload image to server or AWS S3: more…
$('#summernote' ).summernote({
callbacks: {
onImageUpload: function (files) {
$summernote.summernote('insertNode' , imgNode);
}
}
});
$('#summernote' ).on('summernote.image.upload' , function (we, files) {
$summernote.summernote('insertNode' , imgNode);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
那么此时onImageUpload的具体写法呢?(后端为springMVC):
callbacks : {
onImageUpload : function (files) {
var $files = $(files);
$files.each(function () {
var file = this ;
var data = new FormData();
data.append("file" , file);
$.ajax({
data : data,
type : "POST" ,
url : url,
cache : false ,
contentType : false ,
processData : false ,
success : function (response) {
var json = YUNM.jsonEval(response);
YUNM.debug(json);
YUNM.ajaxDone(json);
if (json[YUNM.keys.statusCode] == YUNM.statusCode.ok) {
if (json[YUNM.keys.result]) {
var imageUrl = json[YUNM.keys.result].completeSavePath;
$this .summernote('insertImage' , imageUrl, function ($image) {
});
}
}
},
error : YUNM.ajaxError
});
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
注释当中加的很详细,这里把其他关联的代码一并贴出,仅供参照。
debug : function (msg) {
if (this ._set.debug) {
if (typeof (console) != "undefined" )
console.log(msg);
else
alert(msg);
}
},
jsonEval : function (data) {
try {
if ($.type(data) == 'string' )
return eval ('(' + data + ')' );
else
return data;
} catch (e) {
return {};
}
},
ajaxError : function (xhr, ajaxOptions, thrownError) {
if (xhr.responseText) {
$.showErr("" + xhr.responseText + "
" );
} else {
$.showErr("Http status: " + xhr.status + " " + xhr.statusText + "
" + "ajaxOptions: " + ajaxOptions + "
"
+ "thrownError: " + thrownError + "
" );
}
},
ajaxDone : function (json) {
if (json[YUNM.keys.statusCode] == YUNM.statusCode.error) {
if (json[YUNM.keys.message]) {
YUNM.debug(json[YUNM.keys.message]);
$.showErr(json[YUNM.keys.message]);
}
} else if (json[YUNM.keys.statusCode] == YUNM.statusCode.timeout) {
YUNM.debug(json[YUNM.keys.message]);
$.showErr(json[YUNM.keys.message] || YUNM.msg("sessionTimout" ), YUNM.loadLogin);
}
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2、后端springMVC文件保存
2.1、为springMVC增加文件的配置
<bean id ="multipartResolver"
class ="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding ="UTF-8" >
<property name ="maxUploadSize" value ="1024000000" > property >
bean >
<mvc:annotation-driven conversion-service ="conversionService" />
<bean id ="conversionService"
class ="org.springframework.format.support.FormattingConversionServiceFactoryBean" >
<property name ="converters" >
<list >
<bean class ="com.honzh.common.plugin.StringToDateConverter" />
<bean class ="com.honzh.common.plugin.CommonsMultipartFileToString" />
list >
property >
bean >
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这里就不做过多介绍了,可参照我之前写的SpringMVC之context-dispatcher.xml,了解基本的控制器
2.2、FileController.java
package com.honzh.spring.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.honzh.common.base.UploadFile;
import com.honzh.spring.service.FileService;
@Controller
@RequestMapping (value = "/file" )
public class FileController extends BaseController {
private static Logger logger = Logger.getLogger(FileController.class);
@Autowired
private FileService fileService;
@RequestMapping ("" )
public void index (HttpServletRequest request, HttpServletResponse response) {
logger.debug("获取上传文件..." );
try {
UploadFile uploadFiles = fileService.saveFile(request);
renderJsonDone(response, uploadFiles);
} catch (Exception e) {
logger.error(e.getMessage());
logger.error(e.getMessage(), e);
renderJsonError(response, "文件上传失败" );
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2.3、FileService.java
package com.honzh.spring.service;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import com.honzh.common.Variables;
import com.honzh.common.base.UploadFile;
import com.honzh.common.util.DateUtil;
@Service
public class FileService {
private static Logger logger = Logger.getLogger(FileService.class);
public UploadFile saveFile (HttpServletRequest request) throws IOException {
logger.debug("获取上传文件..." );
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map fileMap = multipartRequest.getFileMap();
Iterator fileIterator = multipartRequest.getFileNames();
String requestURL = request.getRequestURL().toString();
String prePath = requestURL.substring(0 , requestURL.indexOf(Variables.ctx));
while (fileIterator.hasNext()) {
String fileKey = fileIterator.next();
logger.debug("文件名为:" + fileKey);
MultipartFile multipartFile = fileMap.get(fileKey);
if (multipartFile.getSize() != 0 L) {
validateImage(multipartFile);
UploadFile file = saveImage(multipartFile);
file.setPrePath(prePath);
return file;
}
}
return null ;
}
private UploadFile saveImage (MultipartFile image) throws IOException {
String originalFilename = image.getOriginalFilename();
logger.debug("文件原始名称为:" + originalFilename);
String contentType = image.getContentType();
String type = contentType.substring(contentType.indexOf("/" ) + 1 );
String fileName = DateUtil.getCurrentMillStr() + new Random().nextInt(100 ) + "." + type;
UploadFile file = new UploadFile(Variables.save_directory, fileName);
file.setContentType(contentType);
logger.debug("文件保存路径:" + file.getSaveDirectory());
FileUtils.writeByteArrayToFile(file.getFile(), image.getBytes());
return file;
}
private void validateImage (MultipartFile image) {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
2.4、UploadFile.java
package com.honzh.common.base;
import java.io.File;
import com.honzh.common.Variables;
public class UploadFile {
private String saveDirectory;
private String fileName;
private String contentType;
private String prePath;
private String completeSavePath;
private String relativeSavePath;
public UploadFile (String saveDirectory, String filesystemName) {
this .saveDirectory = saveDirectory;
this .fileName = filesystemName;
}
public String getFileName () {
return fileName;
}
public String getSaveDirectory () {
return saveDirectory;
}
public String getContentType () {
return contentType;
}
public void setContentType (String contentType) {
this .contentType = contentType;
}
public String getPrePath () {
if (prePath == null ) {
return "" ;
}
return prePath;
}
public void setPrePath (String prePath) {
this .prePath = prePath;
setCompleteSavePath(prePath + getRelativeSavePath());
}
public String getCompleteSavePath () {
return completeSavePath;
}
public void setCompleteSavePath (String completeSavePath) {
this .completeSavePath = completeSavePath;
}
public String getRelativeSavePath () {
return relativeSavePath;
}
public void setRelativeSavePath (String relativeSavePath) {
this .relativeSavePath = relativeSavePath;
}
public void setSaveDirectory (String saveDirectory) {
this .saveDirectory = saveDirectory;
}
public void setFileName (String fileName) {
this .fileName = fileName;
}
public File getFile () {
if (getSaveDirectory() == null || getFileName() == null ) {
return null ;
} else {
setRelativeSavePath(Variables.ctx + "/" + Variables.upload + "/" + getFileName());
return new File(getSaveDirectory() + "/" + getFileName());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
后端文件保存方法也非常简单,懂Java的同学都可以看得懂,那么对于后端不使用springmvc的同学,你可以再找找方法。
辛苦的介绍完前两节后,我们来一个动态图看一下效果吧!
这里,我们再回顾一下summernote所在的form表单,其中还包含了一个普通file的input标签,也就是说,该form还需要上传一张项目封面。
先看一下form的属性:
enctype:”multipart/form-data”,表明为文件类型的form保存
iframeCallback方法,稍候详细介绍,主要是对有文件上传的form表单进行封装。
1、iframeCallback
function iframeCallback (form, callback) {
YUNM.debug("带文件上传处理" );
var $form = $(form), $iframe = $("#callbackframe" );
var data = $form.data('bootstrapValidator' );
if (data) {
if (!data.isValid()) {
return false ;
}
}
$("div.summernote" , $form).each(function () {
var $this = $(this );
if (!$this .summernote('isEmpty' )) {
var editor = " ) + "' />" ;
$form.append(editor);
} else {
$.showErr("请填写项目详情" );
return false ;
}
});
if ($iframe.size() == 0 ) {
$iframe = $("" ).appendTo("body" );
}
if (!form.ajax) {
$form.append(' ' );
}
form.target = "callbackframe" ;
_iframeResponse($iframe[0 ], callback || YUNM.ajaxDone);
}
function _iframeResponse (iframe, callback) {
var $iframe = $(iframe), $document = $(document);
$document.trigger("ajaxStart" );
$iframe.bind("load" , function (event) {
$iframe.unbind("load" );
$document.trigger("ajaxStop" );
if (iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
iframe.src == "javascript:'';" ) {
return ;
}
var doc = iframe.contentDocument || iframe.document;
if (doc.readyState && doc.readyState != 'complete' )
return ;
if (doc.body && doc.body.innerHTML == "false" )
return ;
var response;
if (doc.XMLDocument) {
response = doc.XMLDocument;
} else if (doc.body) {
try {
response = $iframe.contents().find("body" ).text();
response = jQuery.parseJSON(response);
} catch (e) {
response = doc.body.innerHTML;
}
} else {
response = doc;
}
callback(response);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
贴上全部代码以供参考,但是这里我们只讲以下部分:
$("div.summernote" , $form).each(function () {
var $this = $(this );
if (!$this .summernote('isEmpty' )) {
var editor = " ) + "' />" ;
$form.append(editor);
} else {
$.showErr("请填写项目详情" );
return false ;
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
通过form获取到summernote对象$this 后,通过!$this.summernote('isEmpty')
来判断用户是否对富文本编辑器有内容上的填写,保证不为空,为空时,就弹出提示信息。
$this.summernote('code')
可获得summernote编辑器的html内容,将其封装到input对象中,name为前文中div提供的name,供后端使用。
这里其他地方就不做多解释了,详细可参照Bootstrap wysiwyg富文本数据如何保存到mysql。
保存到数据库中是什么样子呢?
<p > <img src ="http://localhost:8080/ymeng/upload/2016033117093076.jpeg" style ="" > p ><p > <br > p ><p > 你好,有兴趣可以加入到沉默王二的群啊<br > p >
页面效果为:
2、新版iframeCallback方法
var $form = $(form), $iframe = $("#callbackframe" );
YUNM.debug("验证其他简单组件" );
var data = $form.data('bootstrapValidator' );
if (data) {
if (!data.isValid()) {
return false ;
}
}
$("div.summernote" , $form).each(function () {
var $this = $(this );
if ($this .summernote('isEmpty' )) {
} else {
YUNM.debug($this .summernote('code' ));
var editor = " )) + "' />" ;
$form.append(editor);
}
});
YUNM.debug("验证通过" );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
比对之前的代码,可以发现代码有两处发生了变化:
当summernote为空时,之前没有做在bootstrap的validator中,是因为还没有搞清楚summernote这种非input标签在validator中的使用,下面会做详细说明。
对summernote的内容加上了base64编码处理,这会有很多好处,稍候介绍。
3、base64的使用方法
js端我在Bootstrap wysiwyg富文本数据如何保存到mysql这篇文章中做了说明,此处不再说明。
可能会有同学需要JavaScript端的base64编码,而需要在springMVC后端使用base64的解码,那么此处介绍一个jar包(Java Base64.jar),使用方法很简单,下载好jar包后,就可以使用如下方法解码:
import it.sauronsoftware.base64.Base64;
deal.setDescription(StringEscapeUtils.escapeHtml(Base64.decode(description, "utf-8" )));
首先,base64的import如上,来自于javabase64.jar包。
decode的编码前端js使用的utf-8,此处自然也用utf-8。
至于StringEscapeUtils类,也是一个非常实用的工具类,有兴趣的可详细关注一下(主要可以对html等等特殊标签进行转义)。
4、summernote加入到bootstrap validator中
<div class ="form-group" >
for="" class ="col-md-1 control-label" >项目详情
<div class ="col-md-10" >
<div class ="summernote" name ="description" data-bv-excluded="false" data-bv-notempty placeholder="请对项目进行详细的描述,使更多的人了解你的云梦"
action="${ctx}/file" >${deal.description}div >
div >
div >
注意data-bv-excluded=”false”(由于summernote使用了div作为form表单的呈现形式,非一般的input标签,所以此处要将该name=”description”的field标识为非excluded,默认的validator是不对“[‘:disabled’, ‘:hidden’, ‘:not(:visible)’]”三种标签做处理的,而summernote会默认作为disabled的一种,那么设置上data-bv-excluded=”false” 后,validator将会对summernote做非空的判断)、data-bv-notempty属性。
当然有了上述两个属性后,并不能保证validator的有效性,那么接下来,请继续看。
onChange : function (contents, $editable) {
if ($this .parents().length > 0 ) {
var $form = $this .parents().find("form.required-validate" , $p);
if ($form.length > 0 ) {
var data = $form.data('bootstrapValidator' );
YUNM.debug($this .summernote('isEmpty' ));
if ($this .summernote('isEmpty' )) {
data.updateStatus($this .attr("name" ), 'INVALID' );
} else {
data.updateStatus($this .attr("name" ), 'VALID' );
}
}
}
},
onInit : function () {
if ($this .parents().length > 0 ) {
var $form = $this .parents().find("form.required-validate" , $p);
if ($form.length > 0 ) {
var data = $form.data('bootstrapValidator' );
if (!$this .summernote('isEmpty' )) {
data.updateStatus($this .attr("name" ), 'VALID' );
}
}
}
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在summernote的callbacks中加入onChange 、onInit,当文本域发生变化、初始化时,对summernote在form中的验证字段进行状态的更新,validator中使用updateStatus方法。
updateStatus: function (field, status, validatorName) {
OK,等补上以上两个内容后,整个summernote就完整了。