javascript
JSP 技巧整理
文章目錄
- 1 JSP 標準標簽庫(JSTL)
- 1.1 時間戳顯示
- 1.2 引用其他jsp文件
- 1.3 spring支持的表單標簽
- 2 JavaScript 插件
- 2.1 jQuery Validation 表單驗證框架
- 頁面引用
- 默認校驗規則說明
- 使用案例
- 初始化
- 自定義校驗規則
- 封裝校驗器
- 2.2 jQuery iCheck 單選/復選美化插件
- 頁面引用
- 激活 iCheck
- 回調事件
- 方法
- 案例代碼
- 全選
- 判斷是否選中
- 2.3 jQuery Datatables 表格插件
- 頁面引用
- 分頁查詢案例
- 服務端
- MyBatis 映射文件關鍵代碼
- 定義數據訪問接口
- 定義通用的分頁展示對象
- Dao實現代碼
- Controller層關鍵代碼
- 客戶端
- 以上內容參考以下文檔完成
- 2.4 jQuery TreeTable 樹形表格插件
- 頁面引用
- 接口
- 配置參數
- 屬性說明
- 使用方法
- HTML 結構代碼
- 注意事項
- 演示效果
- 2.4 jQuary DateTime 時間工具類
- 2.5 jQuery zTree 樹插件
- 頁面引用
- 使用方法
- HTML 結構代碼
- 服務器關鍵代碼
- 演示效果
- 2.6 jQuery Dropzone 拖拽上傳插件
- 頁面引用
- 啟用 Dropzone
- 配置 Dropzone
- 配置參數
- 國際化
- 常用事件
- 使用案例
- 服務端支持
- POM
- 配置 spring-mvc.xml
- Controller關鍵代碼
- 2.7 jQuary wangEditor 富文本編輯器
- 什么是富文本編輯器
- 頁面引入
- 啟用 wangEditor
- 效果演示
- 服務端圖片上傳支持
- 控制器關鍵代碼參考
- 2.7 jQuery nth-tabs 多功能選項卡插件
- 2.7.1 其他依賴
- 2.7.2 使用說明
- 2.7.2.1 CSS
- 2.7.2.2 JS
- 2.7.2.3 html
- 2.7.2.4 初始化
- 2.7.2.5 添加一個選項卡
- 2.7.2.6 添加一個不可關閉的選項卡
- 2.7.2.7 添加多個選項卡
- 2.7.2.8 刪除一個選項卡
- 2.7.2.9 刪除其他選項卡
- 2.7.2.10 刪除所有選項卡
- 2.7.2.11 切換到指定選項卡
- 2.7.2.12 定位到當前選項卡
- 2.7.2.13 左滑動
- 2.7.2.14 右滑動
- 2.7.2.15 獲取左右滑動步值
- 2.7.2.16 獲取當前選項卡ID
- 2.7.2.17 獲取所有選項卡寬度
- 2.7.2.18 獲取所有選項卡
- 2.7.3 附:群里提供的版本
- 2.7.3.1 CSS
- 2.7.3.2 JS
- 2.7.3.3 html
- 2.7.3.4 初始化
- 2.7.3.5 添加一個選項卡
1 JSP 標準標簽庫(JSTL)
1.1 時間戳顯示
| type | DATE, TIME, 或 BOTH | 否 | date |
| dateStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
| timeStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
| pattern | 自定義格式模式 | 否 | 無 |
| timeZone | 顯示日期的時區 | 否 | 默認時區 |
| var | 存儲格式化日期的變量名 | 否 | 顯示在頁面 |
| scope | 存儲格式化日志變量的范圍 | 否 | 頁面 |
1.2 引用其他jsp文件
<jsp:include page="includes/footer.jsp"/>1.3 spring支持的表單標簽
《走向單體地獄(八):Spring MVC 》
中的 Spring MVC 表單標簽庫 和 Spring MVC @ModelAttribute聯合應用
2 JavaScript 插件
2.1 jQuery Validation 表單驗證框架
頁面引用
<!-- jQuery Validation 1.14.0 --> <script src="/static/assets/plugins/jquery-validation/js/jquery.validate.js"></script> <script src="/static/assets/plugins/jquery-validation/js/additional-methods.js"></script> <script src="/static/assets/plugins/jquery-validation/js/localization/messages_zh.js"></script>默認校驗規則說明
| required | true | 必輸字段 |
| remote | check.php | 使用 ajax 方法調用 check.php 驗證輸入值 |
| true | 必須輸入正確格式的電子郵件 | |
| `url`` | true | 必須輸入正確格式的網址 |
| date | rue | 必須輸入正確格式的日期 |
| dateISO | true | 必須輸入正確格式的日期(ISO),例如:2009-06-23,1998/01/22 只驗證格式,不驗證有效性 |
| number | true | 必須輸入合法的數字(負數,小數) |
| digits | true | 必須輸入整數 |
| creditcard | 必須輸入合法的信用卡號 | |
| equalTo | #field | 輸入值必須和 #field 相同 |
| accept | 輸入擁有合法后綴名的字符串(上傳文件的后綴) | |
| maxlength | n | 輸入長度最多是n的字符串(漢字算一個字符) |
| minlength | m | 輸入長度最小是10的字符串(漢字算一個字符) |
| rangelength | [n,m] | 輸入長度必須介于 n 和 m 之間的字符串")(漢字算一個字符) |
| range | [n,m] | 輸入值必須介于 n和 m 之間 |
| max | n | 輸入值不能大于 n |
| min | m | 輸入值不能小于 m |
使用案例
初始化
給input元素的class 添加校驗class
<input path="username" class="form-control required" placeholder="用戶名" />定義初始化方法并調用即可
/*** 初始化 jquery validation*/ var handlerInitValidate = function () {$("#inputForm").validate({errorElement: 'span', //定義錯誤元素為span 位于 #inputForm 表單中errorClass: 'help-block', //定義錯誤樣式的class為 help-blockerrorPlacement: function (error, element) {//定義觸發錯誤后的action error是上面定義的錯誤元素 element是錯誤觸發元素既input元素element.parent().parent().attr("class", "form-group has-error"); //替換樣式error.insertAfter(element); //把error元素插入input元素后面}}); };自定義校驗規則
//手機號校驗規則 $.validator.addMethod("mobile", function(value, element) {var length = value.length;var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;return this.optional(element) || (length == 11 && mobile.test(value)); }, "手機號碼格式錯誤");封裝校驗器
/*** 函數對象 之所以要聲明對象,是利用了對象的閉包特性,保證安全性*/ var Validate = function () {/*** 初始化 jquery validation*/var handlerInitValidate = function () {$("#inputForm").validate({errorElement: 'span', //定義錯誤元素為span 位于 #inputForm 表單中errorClass: 'help-block', //定義錯誤樣式的class為 help-blockerrorPlacement: function (error, element) {//定義觸發錯誤后的action error是上面定義的錯誤元素 element是錯誤觸發元素既input元素element.parent().parent().attr("class", "form-group has-error"); //替換樣式error.insertAfter(element); //把error元素插入input元素后面}});};/*** 增加自定義驗證規則*/var handlerInitCustomValidate = function () {$.validator.addMethod("mobile", function(value, element) {var length = value.length;var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;return this.optional(element) || (length == 11 && mobile.test(value));}, "手機號碼格式錯誤");};/*** return 外面的都是私有方法,return里面的都是公共方法*/return {/*** 初始化*/init: function () {handlerInitCustomValidate();handlerInitValidate();}}}();$(document).ready(function () {Validate.init(); });2.2 jQuery iCheck 單選/復選美化插件
頁面引用
CSS 部分
<!-- iCheck for checkboxes and radio inputs --> <link rel="stylesheet" href="/static/assets/plugins/iCheck/all.css">JS 部分
<!-- iCheck 1.0.1 --> <script src="/static/assets/plugins/iCheck/icheck.min.js"></script>激活 iCheck
默認情況下 iCheck 是不生效的,需要使用 JS 代碼激活,此過程可以指定 iCheck 的皮膚,案例代碼如下:
CSS 部分
JS 部分
// 激活 iCheck $('input[type="checkbox"].minimal, input[type="radio"].minimal').iCheck({checkboxClass: 'icheckbox_minimal-blue',radioClass : 'iradio_minimal-blue' });回調事件
iCheck 提供了大量回調事件,都可以用來監聽 change 事件
| ifClicked | 用戶點擊了自定義的輸入框或與其相關聯的 label |
| ifChanged | 輸入框的 checked 或 disabled 狀態改變了 |
| ifChecked | 輸入框的狀態變為 checked |
| ifUnchecked | checked 狀態被移除 |
| ifDisabled | 輸入框狀態變為 disabled |
| `ifEnabled | disabled 狀態被移除 |
| ifCreated | 輸入框被應用了 iCheck 樣式 |
| ifDestroyed | iCheck 樣式被移除 |
使用 on()方法綁定事件:
$('input').on('ifChecked', function(event){alert(event.type + ' callback'); });方法
下面這些方法可以用來通過編程方式改變輸入框狀態(可以使用任何選擇器):
- $(‘input’).iCheck(‘check’);:將輸入框的狀態設置為 checked
- $(‘input’).iCheck(‘uncheck’);:移除 checked 狀態
- $(‘input’).iCheck(‘toggle’);:toggle checked state
- $(‘input’).iCheck(‘disable’);:將輸入框的狀態設置為 disabled
- $(‘input’).iCheck(‘enable’);:移除 disabled 狀態
- $(‘input’).iCheck(‘update’);:apply input changes, which were done outside the plugin
- $(‘input’).iCheck(‘destroy’);:移除 iCheck 樣式
案例代碼
全選
_checkboxMaster = $(".checkbox-master");//主選框 _checkbox = $("tbody").find("[type='checkbox']").not("[disabled]");//所有子選框 _checkboxMaster.on("ifClicked", function (e) {//全選事件// 當前狀態已選中,點擊后應取消選擇if (e.target.checked) {_checkbox.iCheck("uncheck");}// 當前狀態未選中,點擊后應全選else {_checkbox.iCheck("check");} });判斷是否選中
_checkbox.each(function () {// 判斷是否選中var delFlag = $(this).is(":checked");if (delFlag) {_idArray.push($(this).attr("id"));} });2.3 jQuery Datatables 表格插件
中文網站:http://www.datatables.club/
實例索引:http://www.datatables.club/example/
參考手冊:http://www.datatables.club/manual/
幫助文檔:http://www.datatables.club/reference/
頁面引用
CSS 部分
<!-- DataTables --> <link rel="stylesheet" href="/static/assets/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css">JS 部分
<!-- DataTables --> <script src="/static/assets/bower_components/datatables.net/js/jquery.dataTables.min.js"></script> <script src="/static/assets/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>使用,啟用 0 配置模式
$('#dataTable').DataTable();分頁查詢案例
服務端
MyBatis 映射文件關鍵代碼
DataTables 分頁需要提供查詢數據的總筆數,以下為查詢總筆數的關鍵代碼:
<select id="count" resultType="java.lang.Integer">SELECT COUNT(*) FROM tb_user </select>這里我們使用 MySQL 作為數據庫,由于需要傳入分頁參數,這里我們還使用了 Map 作為查詢參數類型,以下為 MySQL 分頁查詢的關鍵代碼:
<select id="page" resultType="TbUser" parameterType="java.util.Map">SELECT<include refid="tbUserColumns" />FROMtb_user AS a LIMIT #{page}, #{pageSize} </select>定義數據訪問接口
/*** 分頁查詢* @param params* @return*/ List<TbUser> page(Map<String, Object> params);/*** 查詢總數* @return*/ int count();定義通用的分頁展示對象
創建一個名為 PageInfo 的分頁數據展示對象,代碼如下:
package com.funtl.my.shop.commons.dto;import com.funtl.my.shop.commons.persistence.BaseEntity;import java.io.Serializable; import java.util.List;/*** 分頁展示對象* <p>Title: PageInfo</p>* <p>Description: </p>** @author Lusifer* @version 1.0.0* @date 2018/6/21 5:17*/ public class PageInfo<T extends BaseEntity> implements Serializable {private int draw; //標識private int recordsTotal; //未過濾數據總數private int recordsFiltered; //過濾后的數據總數private List<T> data; //返回數據private String error; //錯誤提示public int getDraw() {return draw;}public void setDraw(int draw) {this.draw = draw;}public int getRecordsTotal() {return recordsTotal;}public void setRecordsTotal(int recordsTotal) {this.recordsTotal = recordsTotal;}public int getRecordsFiltered() {return recordsFiltered;}public void setRecordsFiltered(int recordsFiltered) {this.recordsFiltered = recordsFiltered;}public List<T> getData() {return data;}public void setData(List<T> data) {this.data = data;}public String getError() {return error;}public void setError(String error) {this.error = error;} }Dao實現代碼
@Override public PageInfo<TbUser> page(Map<String, Object> params) {PageInfo<TbUser> pageInfo = new PageInfo<>();int count = tbUserDao.count();List<TbUser> tbUsers = tbUserDao.page(params);pageInfo.setRecordsTotal(count);pageInfo.setRecordsFiltered(count);pageInfo.setData(tbUsers);return pageInfo; }Controller層關鍵代碼
/*** 分頁查詢** @param request* @return*/ @ResponseBody @RequestMapping(value = "page", method = RequestMethod.GET) public PageInfo<TbUser> page(HttpServletRequest request) {String draw = request.getParameter("draw");int start = Integer.parseInt(request.getParameter("start"));int length = Integer.parseInt(request.getParameter("length"));Map<String, Object> params = new HashMap<>();params.put("page", start);params.put("pageSize", length);PageInfo<TbUser> pageInfo = tbUserService.page(params);pageInfo.setDraw(draw == null ? 0 : Integer.parseInt(draw));return pageInfo; }客戶端
使用 DataTables 分頁功能,需要開啟一些列的相關配置,分頁的數據結果是由 Ajax 請求獲取并解析 JSON格式數據自動封裝進表格的,代碼如下:
$("#dataTable").DataTable({// 是否開啟本地分頁"paging": true,// 是否開啟本地排序"ordering": false,// 是否顯示左下角信息"info": true,// 是否允許用戶改變表格每頁顯示的記錄數"lengthChange": false,// 是否顯示處理狀態(排序的時候,數據很多耗費時間長的話,也會顯示這個)"processing": true,// 是否允許 DataTables 開啟本地搜索"searching": false,// 是否開啟服務器模式"serverSide": true,// 控制 DataTables 的延遲渲染,可以提高初始化的速度"deferRender": true,// 增加或修改通過 Ajax 提交到服務端的請求數據"ajax": {"url": "/user/page"},// 分頁按鈕顯示選項"pagingType": "full_numbers",// 設置列的數據源"columns": [{"data": function (row, type, val, meta) {return '<input id="' + row.id + '" type="checkbox" class="minimal" />';}},{"data": "id"},{"data": "username"},{"data": "phone"},{"data": "email"},{"data": "updated"},{"data": function (row, type, val, meta) {return '<a href="#" type="button" class="btn btn-sm btn-default"><i class="fa fa-search"></i> 查看</a> ' +'<a href="#" type="button" class="btn btn-sm btn-primary"><i class="fa fa-edit"></i> 編輯</a> ' +'<a href="#" type="button" class="btn btn-sm btn-danger"><i class="fa fa-trash-o"></i> 刪除</a>'}}],// 表格重繪的回調函數,激活iCheck插件"drawCallback": function (settings) {App.initCheckbox();},// 國際化"language": {"sProcessing": "處理中...","sLengthMenu": "顯示 _MENU_ 項結果","sZeroRecords": "沒有匹配結果","sInfo": "顯示第 _START_ 至 _END_ 項結果,共 _TOTAL_ 項","sInfoEmpty": "顯示第 0 至 0 項結果,共 0 項","sInfoFiltered": "(由 _MAX_ 項結果過濾)","sInfoPostFix": "","sSearch": "搜索:","sUrl": "","sEmptyTable": "表中數據為空","sLoadingRecords": "載入中...","sInfoThousands": ",","oPaginate": {"sFirst": "首頁","sPrevious": "上頁","sNext": "下頁","sLast": "末頁"},"oAria": {"sSortAscending": ": 以升序排列此列","sSortDescending": ": 以降序排列此列"}} })以上內容參考以下文檔完成
配置選項:http://www.datatables.club/reference/option/
服務器處理:http://www.datatables.club/manual/server-side.html
設置列的數據源:https://datatables.net/reference/option/columns.data
國際化:http://www.datatables.club/manual/i18n.html
2.4 jQuery TreeTable 樹形表格插件
treeTable 是跨瀏覽器、性能很高的 jQuery 的樹表組件,它使用非常簡單,只需要引用 jQuery 庫和一個 js 文件,接口也很簡單。
優點:
- 兼容主流瀏覽器:支持 IE6 和 IE6+, Firefox, chrome, Opera, Safari
- 接口簡潔:在普通表格的基礎上增加父子關系的自定義標簽就可以
- 組件性能高:內部實現了只綁定了 table 的事件、使用了 css sprite 合并圖片等
- 提供兩種風格:通過參數來設置風格
頁面引用
CSS 部分
<link rel="stylesheet" href="/static/assets/plugins/treeTable/themes/vsStyle/treeTable.min.css" />JS 部分
<script src="/static/assets/plugins/treeTable/jquery.treeTable.min.js"></script>接口
配置參數
| theme | string | 主題,有兩個選項:default、vsStyle. 默認:default |
| expandLevel | int | 樹表的展開層次. 默認:1 |
| columnl | int | 可控制列的序號. 默認:0,即第一列 |
| onSelectl | function | 擁有 controller 自定義屬性的元素的點擊事件,return false 則中止展開 |
| beforeExpandl | function | 展開子節點前觸發的事件 |
屬性說明
| id | string | 行的 id |
| pId | string | 父行的 id |
| controller | bool | 指定某一個元素是否可以控制行的展開 |
| hasChild | :bool | 指定某一個 tr 元素是否有孩子(動態加載需用到) |
| isFirstOne | bool | 指定某一個 tr 元素是否是第一個孩子(自動生成屬性,只讀) |
| isLastOne | bool | 指定某一個 tr 元素是否是最后一個孩子(自動生成屬性,只讀) |
| prevId | string | 前一個兄弟節點的 id(自動生成屬性,只讀) |
| depth | string | 當前行的深度(自動生成屬性,只讀) |
使用方法
$(function () {$("#treeTable").treeTable({expandLevel: 2,column: 1}); });HTML 結構代碼
<table id="treeTable1" style="width:100%"><tr><td style="width:200px;">標題</td><td>內容</td></tr><tr id="1"><td><span controller="true">1</span></td><td>內容</td></tr><tr id="2" pId="1"><td><span controller="true">2</span></td><td>內容</td></tr><tr id="3" pId="2"><td>3</td><td>內容</td></tr><tr id="4" pId="2"><td>4</td><td>內容</td></tr><tr id="5" pId="4"><td>4.1</td><td>內容</td></tr><tr id="6" pId="1" hasChild="true"><td>5</td><td>注意這個節點是動態加載的</td></tr><tr id="7"><td>8</td><td>內容</td></tr> </table>注意事項
這里的 HTML 結構是經過排序的,每行數據必須緊跟其子類目的數據項,結構類似于:
類目 1類目 1-1類目 1-2... 類目 2類目 2-1 類目 3 類目 4服務端排序代碼如下:
// 調用方法,0 為約定好的根節點 sortList(sourceList, targetList, 0L);/*** 排序* @param sourceList 數據源集合* @param targetList 排序后的集合* @param parentId 當前的父級類目 ID*/ private void sortList(List<TbContentCategory> sourceList, List<TbContentCategory> targetList, Long parentId) {for (TbContentCategory sourceContentCategory : sourceList) {if (sourceContentCategory.getParentId().equals(parentId)) {targetList.add(sourceContentCategory);// 判斷有沒有子節點,有則繼續追加if (sourceContentCategory.getParent()) {for (TbContentCategory tbContentCategory : sourceList) {if (tbContentCategory.getParentId().equals(sourceContentCategory.getId())) {sortList(sourceList, targetList, sourceContentCategory.getId());break;}}}}} }演示效果
2.4 jQuary DateTime 時間工具類
使用時只需要調用 DateTime函數對象中return的公共方法即可
/*** 日期時間工具類* @type {{dateFormat}}*/ var DateTime = function () {var patterns = {PATTERN_ERA: 'G', // Era 標志符 Era strings. For example: "AD" and "BC"PATTERN_YEAR: 'y', // 年PATTERN_MONTH: 'M', // 月份PATTERN_DAY_OF_MONTH: 'd', // 月份的天數PATTERN_HOUR_OF_DAY1: 'k', // 一天中的小時數(1-24)PATTERN_HOUR_OF_DAY0: 'H', // 24 小時制,一天中的小時數(0-23)PATTERN_MINUTE: 'm', // 小時中的分鐘數PATTERN_SECOND: 's', // 秒PATTERN_MILLISECOND: 'S', // 毫秒PATTERN_DAY_OF_WEEK: 'E', // 一周中對應的星期,如星期一,周一PATTERN_DAY_OF_YEAR: 'D', // 一年中的第幾天PATTERN_DAY_OF_WEEK_IN_MONTH: 'F', // 一月中的第幾個星期(會把這個月總共過的天數除以7,不夠準確,推薦用W)PATTERN_WEEK_OF_YEAR: 'w', // 一年中的第幾個星期PATTERN_WEEK_OF_MONTH: 'W', // 一月中的第幾星期(會根據實際情況來算)PATTERN_AM_PM: 'a', // 上下午標識PATTERN_HOUR1: 'h', // 12 小時制 ,am/pm 中的小時數(1-12)PATTERN_HOUR0: 'K', // 和 h 類型PATTERN_ZONE_NAME: 'z', // 時區名PATTERN_ZONE_VALUE: 'Z', // 時區值PATTERN_WEEK_YEAR: 'Y', // 和 y 類型PATTERN_ISO_DAY_OF_WEEK: 'u',PATTERN_ISO_ZONE: 'X'};var week = {'ch': {"0": "\u65e5","1": "\u4e00","2": "\u4e8c","3": "\u4e09","4": "\u56db","5": "\u4e94","6": "\u516d"},'en': {"0": "Sunday","1": "Monday","2": "Tuesday","3": "Wednesday","4": "Thursday","5": "Friday","6": "Saturday"}};/*** 獲取當前時間* @returns {string}*/var handlerGetCurrentTime = function () {var today = new Date();var year = today.getFullYear();var month = today.getMonth() + 1;var day = today.getDate();var hours = today.getHours();var minutes = today.getMinutes();var seconds = today.getSeconds();var timeString = year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;return timeString;};/*** 比較時間大小* time1 > time2 return 1* time1 < time2 return -1* time1 == time2 return 0* @param time1* @param time2* @returns {number}*/var handlerCompareTime = function (time1, time2) {if (Date.parse(time1.replace(/-/g, "/")) > Date.parse(time2.replace(/-/g, "/"))) {return 1;} else if (Date.parse(time1.replace(/-/g, "/")) < Date.parse(time2.replace(/-/g, "/"))) {return -1;} else if (Date.parse(time1.replace(/-/g, "/")) == Date.parse(time2.replace(/-/g, "/"))) {return 0;}};/*** 是否閏年* @param year* @returns {boolean}*/var handlerIsLeapYear = function (year) {return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);};/*** 獲取某個月的天數,從 0 開始* @param year* @param month* @returns {*}*/var handlerGetDaysOfMonth = function (year, month) {return [31, (this.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];};/*** 獲取某個月的天數,從 0 開始* @param year* @param month* @returns {number}*/var handlerGetDaysOfMonth2 = function (year, month) {// 將天置為 0,會獲取其上個月的最后一天month = parseInt(month) + 1;var date = new Date(year, month, 0);return date.getDate();};/*** 距離現在幾天的日期:負數表示今天之前的日期,0 表示今天,整數表示未來的日期* 如 -1 表示昨天的日期,0 表示今天,2 表示后天* @param days* @returns {string}*/var handlerFromToday = function (days) {var today = new Date();today.setDate(today.getDate() + days);var date = today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate();return date;};/*** 格式化日期時間* @param dateTime 需要格式化的日期時間* @param pattern 格式化的模式,如 yyyy-MM-dd hh(HH):mm:ss.S a k K E D F w W z Z* @returns {*}*/var handlerFormat = function (dateTime, pattern) {var date = new Date(dateTime);if (pattern == null || pattern.length == 0) {return date.toLocaleString();}return pattern.replace(/([a-z])\1*/ig, function (matchStr, group1) {var replacement = "";switch (group1) {case patterns.PATTERN_ERA: //Gbreak;case patterns.PATTERN_WEEK_YEAR: //Ycase patterns.PATTERN_YEAR: //yreplacement = date.getFullYear();break;case patterns.PATTERN_MONTH: //Mvar month = date.getMonth() + 1;replacement = (month < 10 && matchStr.length >= 2) ? "0" + month : month;break;case patterns.PATTERN_DAY_OF_MONTH: //dvar days = date.getDate();replacement = (days < 10 && matchStr.length >= 2) ? "0" + days : days;break;case patterns.PATTERN_HOUR_OF_DAY1: //k(1~24)var hours24 = date.getHours();replacement = hours24;break;case patterns.PATTERN_HOUR_OF_DAY0: //H(0~23)var hours24 = date.getHours();replacement = (hours24 < 10 && matchStr.length >= 2) ? "0" + hours24 : hours24;break;case patterns.PATTERN_MINUTE: //mvar minutes = date.getMinutes();replacement = (minutes < 10 && matchStr.length >= 2) ? "0" + minutes : minutes;break;case patterns.PATTERN_SECOND: //svar seconds = date.getSeconds();replacement = (seconds < 10 && matchStr.length >= 2) ? "0" + seconds : seconds;break;case patterns.PATTERN_MILLISECOND: //Svar milliSeconds = date.getMilliseconds();replacement = milliSeconds;break;case patterns.PATTERN_DAY_OF_WEEK: //Evar day = date.getDay();replacement = week['ch'][day];break;case patterns.PATTERN_DAY_OF_YEAR: //Dreplacement = dayOfTheYear(date);break;case patterns.PATTERN_DAY_OF_WEEK_IN_MONTH: //Fvar days = date.getDate();replacement = Math.floor(days / 7);break;case patterns.PATTERN_WEEK_OF_YEAR: //wvar days = dayOfTheYear(date);replacement = Math.ceil(days / 7);break;case patterns.PATTERN_WEEK_OF_MONTH: //Wvar days = date.getDate();replacement = Math.ceil(days / 7);break;case patterns.PATTERN_AM_PM: //avar hours24 = date.getHours();replacement = hours24 < 12 ? "\u4e0a\u5348" : "\u4e0b\u5348";break;case patterns.PATTERN_HOUR1: //h(1~12)var hours12 = date.getHours() % 12 || 12; //0轉為12replacement = (hours12 < 10 && matchStr.length >= 2) ? "0" + hours12 : hours12;break;case patterns.PATTERN_HOUR0: //K(0~11)var hours12 = date.getHours() % 12;replacement = hours12;break;case patterns.PATTERN_ZONE_NAME: //zreplacement = handlerGetZoneNameValue(date)['name'];break;case patterns.PATTERN_ZONE_VALUE: //Zreplacement = handlerGetZoneNameValue(date)['value'];break;case patterns.PATTERN_ISO_DAY_OF_WEEK: //ubreak;case patterns.PATTERN_ISO_ZONE: //Xbreak;default:break;}return replacement;});};/*** 計算一個日期是當年的第幾天* @param date* @returns {number}*/var handlerDayOfTheYear = function (date) {var obj = new Date(date);var year = obj.getFullYear();var month = obj.getMonth(); //從0開始var days = obj.getDate();var daysArr = [31, (this.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];for (var i = 0; i < month; i++) {days += daysArr[i];}return days;};/*** 獲得時區名和值* @param dateObj* @returns {{name: string, value: string}}*/var handlerGetZoneNameValue = function (dateObj) {var date = new Date(dateObj);date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));var arr = date.toString().match(/([A-Z]+)([-+]\d+:?\d+)/);var obj = {'name': arr[1],'value': arr[2]};return obj;};return {getCurrentTime: function () {return handlerGetCurrentTime();},compareTime: function (time1, time2) {return handlerCompareTime(time1, time2);},isLeapYear: function (year) {return handlerIsLeapYear(year);},getDaysOfMonth: function (year, month) {return handlerGetDaysOfMonth(year, month);},getDaysOfMonth2: function (year, month) {return handlerGetDaysOfMonth2(year, month);},fromToday: function (days) {return handlerFromToday(days);},format: function (dateTime, pattern) {return handlerFormat(dateTime, pattern);},dayOfTheYear: function (date) {return handlerDayOfTheYear(date);},getZoneNameValue: function (dateObj) {return handlerGetZoneNameValue(dateObj);}} }();2.5 jQuery zTree 樹插件
zTree 是一個依靠 jQuery 實現的多功能 “樹插件”。優異的性能、靈活的配置、多種功能的組合是 zTree 最大優點,具體用法請參考官方文檔。
頁面引用
CSS 部分
<link rel="stylesheet" href="/static/assets/plugins/jquery-ztree/css/zTreeStyle/zTreeStyle.min.css" />JS 部分
<script src="/static/assets/plugins/jquery-ztree/js/jquery.ztree.core-3.5.min.js"></script>使用方法
開啟 zTree 異步加載數據的功能,案例代碼如下:
var setting = {view: {// 禁止多選selectedMulti: false},async: {// 開啟異步加載功能enable: true,// 遠程訪問地址url: "/content/category/tree/data",// 選擇父節點時會自動將節點中的參數傳回服務器再重新取結果autoParam: ["id"]} };// 初始化 zTree 控件 $.fn.zTree.init($("#myTree"), setting); // 綁定事件 $("#btnModalOk").bind("click", function () {// 獲取 zTree 控件var zTree = $.fn.zTree.getZTreeObj("myTree");// 獲取已選中的節點var nodes = zTree.getSelectedNodes();if (nodes.length == 0) {alert("請先選擇一個父節點");}else {var node = nodes[0];alert(node.id);} });HTML 結構代碼
<ul id="myTree" class="ztree"></ul>服務器關鍵代碼
Controller 層
@ResponseBody @RequestMapping(value = "tree/data", method = RequestMethod.POST) public List<TbContentCategory> treeData(String id) {if (id == null) {id = "0";}List<TbContentCategory> tbContentCategories = tbContentCategoryService.selectByPid(Long.parseLong(id));return tbContentCategories; }Dao 層
查找pid為傳入id的List
演示效果
2.6 jQuery Dropzone 拖拽上傳插件
Dropzone 是一個開源的 JavaScript 庫,提供文件的異步上傳功能,并支持拖拽上傳功能
頁面引用
CSS 部分,其中 basic.min.css 提供了官網的炫酷上傳效果
<link rel="stylesheet" href="/static/assets/plugins/dropzone/min/dropzone.min.css" /> <link rel="stylesheet" href="/static/assets/plugins/dropzone/min/basic.min.css" />JS 部分
<script src="/static/assets/plugins/dropzone/min/dropzone.min.js"></script>啟用 Dropzone
只需要一個 div 元素,用 JavaScript 代碼啟用即可
HTML 結構如下:
<div id="dropz" class="dropzone"></div>JavaScript 啟用代碼如下:
var myDropzone = new Dropzone("#dropz", {url: "/upload",dictDefaultMessage: '拖動文件至此或者點擊上傳', // 設置默認的提示語句paramName: "dropzFile", // 傳到后臺的參數名稱init: function () {this.on("success", function (file, data) {// 上傳成功觸發的事件});} });其中 url 是必須的值,指明文件上傳提交到哪個頁面。其他的值都是可選的,如果使用默認值的話可以省略。
配置 Dropzone
此插件的特色就在于非常靈活,提供了許多可選項、事件等。下面分類介紹常用的配置項。
配置參數
| url | 最重要的參數,指明了文件提交到哪個頁面 |
| method | 默認為 post,如果需要,可以改為 put |
| paramName | 相當于 元素的 name 屬性,默認為 file |
| maxFilesize | 最大文件大小,單位是 MB |
| maxFiles | 默認為 null,可以指定為一個數值,限制最多文件數量 |
| addRemoveLinks | 默認 false。如果設為 true,則會給文件添加一個刪除鏈接 |
| acceptedFiles | 指明允許上傳的文件類型,格式是逗號分隔的 MIME type 或者擴展名。例如:image/*, application/pdf, .psd, .obj |
| uploadMultiple | 指明是否允許 Dropzone 一次提交多個文件。默認為 false。如果設為 true,則相當于 HTML 表單添加 multiple 屬性 |
| headersl | 如果設定,則會作為額外的 header 信息發送到服務器。例如:{“custom-header”: “value”} |
| initl | 一個函數,在 Dropzone 初始化的時候調用,可以用來添加自己的事件監聽器 |
| forceFallback | Fallback 是一種機制,當瀏覽器不支持此插件時,提供一個備選方案。默認為 false。如果設為 true,則強制 fallback |
| fallback | 一個函數,如果瀏覽器不支持此插件則調用 |
國際化
| dictDefaultMessage | 沒有任何文件被添加的時候的提示文本 |
| dictFallbackMessage | allback 情況下的提示文本 |
| dictInvalidInputType | 文件類型被拒絕時的提示文本 |
| dictFileTooBig | 文件大小過大時的提示文本 |
| dictCancelUpload | 取消上傳鏈接的文本 |
| dictCancelUploadConfirmation | 取消上傳確認信息的文本 |
| dictRemoveFile | 移除文件鏈接的文本 |
| dictMaxFilesExceeded | 超過最大文件數量的提示文本 |
常用事件
以下事件接收 file 為第一個參數
| addedfile | 添加了一個文件時發生 |
| removedfile | 一個文件被移除時發生。你可以監聽這個事件并手動從服務器刪除這個文件 |
| uploadprogress | 上傳時按一定間隔發生這個事件。第二個參數為一個整數,表示進度,從 0 到 100。第三個參數是一個整數,表示發送到服務器的字節數。當一個上傳結束時,Dropzone 保證會把進度設為 100。注意:這個函數可能被以同一個進度調用多次 |
| success | 文件成功上傳之后發生,第二個參數為服務器響應 |
| complete | 當文件上傳成功或失敗之后發生 |
| canceled | 當文件在上傳時被取消的時候發生 |
| maxfilesreached | 當文件數量達到最大時發生 |
| maxfilesexceeded | 當文件數量超過限制時發生 |
以下事件接收一個 file list 作為第一個參數(僅當 uploadMultiple 被設為 true 時才會發生)
| successmultiple | 成功上傳 |
| completemultiple | 上傳完畢 |
| cancelmultiple | 上傳失敗 |
特殊事件
| totaluploadprogress | 第一個參數為總上傳進度,第二個參數為總字節數,第三個參數為總上傳字節數。 |
使用案例
var myDropzone = new Dropzone("#dropz", {url: "/upload", // 文件提交地址method: "post", // 也可用putparamName: "file", // 默認為filemaxFiles: 1,// 一次性上傳的文件數量上限maxFilesize: 2, // 文件大小,單位:MBacceptedFiles: ".jpg,.gif,.png,.jpeg", // 上傳的類型addRemoveLinks: true,parallelUploads: 1,// 一次上傳的文件數量//previewsContainer:"#preview", // 上傳圖片的預覽窗口dictDefaultMessage: '拖動文件至此或者點擊上傳',dictMaxFilesExceeded: "您最多只能上傳1個文件!",dictResponseError: '文件上傳失敗!',dictInvalidFileType: "文件類型只能是*.jpg,*.gif,*.png,*.jpeg。",dictFallbackMessage: "瀏覽器不受支持",dictFileTooBig: "文件過大上傳文件最大支持.",dictRemoveLinks: "刪除",dictCancelUpload: "取消",init: function () {this.on("addedfile", function (file) {// 上傳文件時觸發的事件});this.on("success", function (file, data) {// 上傳成功觸發的事件});this.on("error", function (file, data) {// 上傳失敗觸發的事件});this.on("removedfile", function (file) {// 刪除文件時觸發的方法});} });服務端支持
前端工作做完后,后臺需要提供文件上傳支持,我們使用 Spring MVC 來接收上傳的文件
POM
Spring MVC 上傳文件需要 commons-fileupload:commons-fileupload 依賴支持,pom.xml 文件如下:
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.2</version> </dependency>配置 spring-mvc.xml
需要 Spring 注入 multipartResolver 視圖解析器實例,spring-mvc.xml 增加如下配置:
<!-- 上傳文件攔截,設置最大上傳文件大小 10M = 10*1024*1024(B) = 10485760 bytes --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="maxUploadSize" value="10485760"/> </bean>Controller關鍵代碼
package com.funtl.my.shop.web.admin.web.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID;/*** 文件上傳控制器* <p>Title: UploadController</p>* <p>Description: </p>** @author Lusifer* @version 1.0.0* @date 2018/6/27 0:42*/ @Controller public class UploadController {@ResponseBody@RequestMapping(value = "upload", method = RequestMethod.POST)public Map<String, Object> upload(MultipartFile dropzFile, HttpServletRequest request) {Map<String, Object> result = new HashMap<>();// 獲取上傳的原始文件名String fileName = dropzFile.getOriginalFilename();// 設置文件上傳路徑String filePath = request.getSession().getServletContext().getRealPath("/static/upload");// 獲取文件后綴String fileSuffix = fileName.substring(fileName.lastIndexOf("."), fileName.length());// 判斷并創建上傳用的文件夾File file = new File(filePath);if (!file.exists()) {file.mkdir();}// 重新設置文件名為 UUID,以確保唯一file = new File(filePath, UUID.randomUUID() + fileSuffix);try {// 寫入文件dropzFile.transferTo(file);} catch (IOException e) {e.printStackTrace();}// 返回 JSON 數據,這里只帶入了文件名result.put("fileName", file.getName());return result;} }2.7 jQuary wangEditor 富文本編輯器
輕量級 web 富文本編輯器,配置方便,使用簡單。支持 IE10+ 瀏覽器。
官網:http://www.wangEditor.com
文檔:http://www.kancloud.cn/wangfupeng/wangeditor3/332599
源碼:http://github.com/wangfupeng1988/wangEditor
什么是富文本編輯器
富文本編輯器,Rich Text Editor, 簡稱 RTE, 它提供類似于 Microsoft Word 的編輯功能,容易被不會編寫 HTML 的用戶并需要設置各種文本格式的用戶所喜愛。它的應用也越來越廣泛。最先只有 IE 瀏覽器支持,其它瀏覽器相繼跟進,在功能的豐富性來說,還是 IE 強些。雖然沒有一個統一的標準,但對于最基本的功能,各瀏覽器提供的 API 基本一致,從而使編寫一個跨瀏覽器的富文本編輯器成為可能。
頁面引入
CSS 部分
<link rel="stylesheet" href="/static/assets/plugins/wangEditor/wangEditor.min.css" />JS 部分
<script src="/static/assets/plugins/wangEditor/wangEditor.min.js"></script>啟用 wangEditor
只需要一個 div 元素,用 JavaScript 代碼啟用即可
HTML 結構如下:
JavaScript 啟用代碼如下:
var E = window.wangEditor; var editor = new E('#editor'); editor.create();效果演示
配置完成后,在瀏覽器端顯示如下:
服務端圖片上傳支持
配置方式同 Dropzone 圖片上傳插件,參考官方手冊完成
控制器關鍵代碼參考
package com.funtl.my.shop.web.admin.web.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID;/*** 文件上傳控制器* <p>Title: UploadController</p>* <p>Description: </p>** @author Lusifer* @version 1.0.0* @date 2018/6/27 14:32*/ @Controller public class UploadController {private static final String UPLOAD_PATH = "/static/upload/";/*** 文件上傳** @return*/@ResponseBody@RequestMapping(value = "upload", method = RequestMethod.POST)public Map<String, Object> upload(MultipartFile editorFile, HttpServletRequest request) {Map<String, Object> result = new HashMap<>();// 獲取文件后綴String fileName = editorFile.getOriginalFilename();String fileSuffix = fileName.substring(fileName.lastIndexOf("."));// 文件存放路徑String filePath = request.getSession().getServletContext().getRealPath(UPLOAD_PATH);// 判斷路徑是否存在,不存在則創建文件夾File file = new File(filePath);if (!file.exists()) {file.mkdir();}// 將文件寫入目標file = new File(filePath, UUID.randomUUID() + fileSuffix);try {editorFile.transferTo(file);} catch (IOException e) {e.printStackTrace();}// 獲取服務端路徑String serverPath = String.format("%s://%s:%s%s%s", request.getScheme(), request.getServerName(), request.getServerPort(), request.getContextPath(), UPLOAD_PATH);// 返回給 wangEditor 的數據格式result.put("errno", 0);result.put("data", new String[]{serverPath + file.getName()});return result;} } 相比 Dropzone 圖片上傳插件 一節,控制器代碼的主要差別在于接口返回的數據格式,官方要求的格式如下:{// errno 即錯誤代碼,0 表示沒有錯誤。// 如果有錯誤,errno != 0,可通過下文中的監聽函數 fail 拿到該錯誤碼進行自定義處理"errno": 0,// data 是一個數組,返回若干圖片的線上地址"data": ["圖片1地址","圖片2地址","……"] }2.7 jQuery nth-tabs 多功能選項卡插件
2.7.1 其他依賴
滾動條:jquery.scrollbar
字體圖標:font-awesome
2.7.2 使用說明
整體基于Bootstrap整合
2.7.2.1 CSS
<link href="https://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/jquery.scrollbar/0.2.11/jquery.scrollbar.min.css" rel="stylesheet"> <link href="css/nth.tabs.min.css" rel="stylesheet">2.7.2.2 JS
<script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> <script src="https://cdn.bootcss.com/jquery.scrollbar/0.2.11/jquery.scrollbar.min.js"></script> <script src="js/nth.tabs.min.js"></script>2.7.2.3 html
<div class="nth-tabs" id="custom-id"></div>2.7.2.4 初始化
nthTabs = $("#custom-id").nthTabs();2.7.2.5 添加一個選項卡
nthTabs.addTab({id:'a',title:'孫悟空',content:'看我七十二變', });2.7.2.6 添加一個不可關閉的選項卡
nthTabs.addTab({id:'a',title:'孫悟空',content:'看我七十二變',allowClose:false, });2.7.2.7 添加多個選項卡
nthTabs.addTab({id:'a',title:'孫悟空',content:'看我七十二變', }).addTab({id:'b',title:'孫悟空二號',content:'看我七十三變', });2.7.2.8 刪除一個選項卡
nthTabs.delTab('id');2.7.2.9 刪除其他選項卡
nthTabs.delOtherTab();2.7.2.10 刪除所有選項卡
nthTabs.delAllTab();2.7.2.11 切換到指定選項卡
nthTabs.setActTab(id);2.7.2.12 定位到當前選項卡
nthTabs.locationTab();2.7.2.13 左滑動
$('.roll-nav-left').click();2.7.2.14 右滑動
$('.roll-nav-right').click();2.7.2.15 獲取左右滑動步值
nthTabs.getMarginStep();2.7.2.16 獲取當前選項卡ID
nthTabs.getActiveId();2.7.2.17 獲取所有選項卡寬度
nthTabs.getAllTabWidth();2.7.2.18 獲取所有選項卡
nthTabs.nthTabs.getTabList();2.7.3 附:群里提供的版本
群里提供的版本是為了適應 AdminLTE 而修改的版本,使用方式略有不同
2.7.3.1 CSS
<link href="https://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/jquery.scrollbar/0.2.11/jquery.scrollbar.min.css" rel="stylesheet"> <link href="css/nth.tabs.css" rel="stylesheet">主要修改了 nth.tabs.css 部分樣式以適應 AdminLTE
2.7.3.2 JS
<script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> <script src="https://cdn.bootcss.com/jquery.scrollbar/0.2.11/jquery.scrollbar.min.js"></script> <script src="js/nth.tabs.min.js"></script> <script src="js/nth-tabs.js"></script>主要增加了 nth-tabs.js 這個二次封裝的自定義工具類
var NthTabs = function () {var nthTabs;var handleInit = function () {nthTabs = $("#editor-tabs").nthTabs();};var handleHome = function (src) {nthTabs.addTab({id: "home",title: "首頁",content: '<iframe name="iframe0" width="100%" height="737" src="' + src + '" frameborder="0"></iframe>',allowClose: false,active: true});};var handleAddTab = function (id, title, src) {// 判斷選項卡是否存在var hasTab = false;var nthTabList = nthTabs.getTabList();for (var i = 0 ; i < nthTabList.length ; i++) {var nthTab = nthTabList[i];if (nthTab.id == "#" + id) {nthTabs.setActTab(id);hasTab = true;break;}}// 選項卡已存在,返回if (hasTab) {return;}nthTabs.addTab({id: id,title: title,content: '<iframe name="iframe' + id + '" width="100%" height="737" src="' + src + '" frameborder="0"></iframe>'});nthTabs.setActTab(id);};return {init: function () {handleInit();},home: function (src) {handleHome(src)},addTab: function (id, title, src, allowClose, active) {handleAddTab(id, title, src, allowClose, active);}}; }();jQuery(document).ready(function () {NthTabs.init(); });2.7.3.3 html
<div class="nth-tabs common-bg" id="editor-tabs"></div>2.7.3.4 初始化
$(function () {NthTabs.home("/path"); });2.7.3.5 添加一個選項卡
NthTabs.addTab('id', 'name', '/path');總結
- 上一篇: 2020 年最受程序员欢迎的 20 个
- 下一篇: 【坑】javascript中appNam