LayUI后台管理与综合示例
一、LayUI介绍
layui(谐音:类UI) 是一款采用自身模块规范编写的前端 UI 框架,遵循原生 HTML/CSS/JS 的书写与组织形式,门槛极低,拿来即用。其外在极简,却又不失饱满的内在,体积轻盈,组件丰盈,从核心代码到 API 的每一处细节都经过精心雕琢,非常适合界面的快速开发。layui 首个版本发布于2016年金秋,她区别于那些基于 MVVM 底层的 UI 框架,却并非逆道而行,而是信奉返璞归真之道。准确地说,她更多是为服务端程序员量身定做,你无需涉足各种前端工具的复杂配置,只需面对浏览器本身,让一切你所需要的元素与交互,从这里信手拈来。
layui 兼容人类正在使用的全部浏览器(IE6/7除外),可作为 PC 端后台系统与前台界面的速成开发方案。
1.1、资源
git:https://github.com/sentsin/layui
二、Hello World
2.1、引入UI框架
下载layui将dest中的内容添加到项目中
项目:
2.2、后台布局
新建admin.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>layout 后台大布局 - Layui</title> <link rel="stylesheet" href="js/layui/css/layui.css"> </head> <body class="layui-layout-body"> <div class="layui-layout layui-layout-admin"> <div class="layui-header"> <div class="layui-logo">layui 后台布局</div> <!-- 头部区域(可配合layui已有的水平导航) --> <ul class="layui-nav layui-layout-left"> <li class="layui-nav-item"> <a href="">控制台</a> </li> <li class="layui-nav-item"> <a href="">商品管理</a> </li> <li class="layui-nav-item"> <a href="">用户</a> </li> <li class="layui-nav-item"> <a href="javascript:;">其它系统</a> <dl class="layui-nav-child"> <dd> <a href="">邮件管理</a> </dd> <dd> <a href="">消息管理</a> </dd> <dd> <a href="">授权管理</a> </dd> </dl> </li> </ul> <ul class="layui-nav layui-layout-right"> <li class="layui-nav-item"> <a href="javascript:;"> <img src="http://t.cn/RCzsdCq" class="layui-nav-img"> 贤心 </a> <dl class="layui-nav-child"> <dd> <a href="">基本资料</a> </dd> <dd> <a href="">安全设置</a> </dd> </dl> </li> <li class="layui-nav-item"> <a href="">退了</a> </li> </ul> </div> <div class="layui-side layui-bg-black"> <div class="layui-side-scroll"> <!-- 左侧导航区域(可配合layui已有的垂直导航) --> <ul class="layui-nav layui-nav-tree" lay-filter="test"> <li class="layui-nav-item layui-nav-itemed"> <a class="" href="javascript:;">所有商品</a> <dl class="layui-nav-child"> <dd> <a href="javascript:;">列表一</a> </dd> <dd> <a href="javascript:;">列表二</a> </dd> <dd> <a href="javascript:;">列表三</a> </dd> <dd> <a href="http://www.baidu.com">超链接</a> </dd> </dl> </li> <li class="layui-nav-item"> <a href="javascript:;">解决方案</a> <dl class="layui-nav-child"> <dd> <a href="javascript:;">列表一</a> </dd> <dd> <a href="javascript:;">列表二</a> </dd> <dd> <a href="">超链接</a> </dd> </dl> </li> <li class="layui-nav-item"> <a href="">云市场</a> </li> <li class="layui-nav-item"> <a href="">发布商品</a> </li> </ul> </div> </div> <div class="layui-body"> <!-- 内容主体区域 --> <div style="padding: 15px;"> <table id="demo" lay-filter="test"></table> </div> </div> <div class="layui-footer"> <!-- 底部固定区域 --> © layui.com - 底部固定区域 </div> </div> <script src="js/layui/layui.all.js"></script> <script> //JavaScript代码区域 layui.use('element', function() { var element = layui.element; }); layui.use('table', function(){ var table = layui.table; //第一个实例 table.render({ elem: '#demo' ,height: 315 ,url: '/demo/table/user/' //数据接口 ,page: true //开启分页 ,cols: [[ //表头 {field: 'id', title: 'ID', width:80, sort: true, fixed: 'left'} ,{field: 'username', title: '用户名', width:80} ,{field: 'sex', title: '性别', width:80, sort: true} ,{field: 'city', title: '城市', width:80} ,{field: 'sign', title: '签名', width: 177} ,{field: 'experience', title: '积分', width: 80, sort: true} ,{field: 'score', title: '评分', width: 80, sort: true} ,{field: 'classify', title: '职业', width: 80} ,{field: 'wealth', title: '财富', width: 135, sort: true} ]] }); }); </script> </body> </html>
结果
2.3、后台服务
2.3.1、封装数据R.java
package com.zhangguo.utils; public class R { /**响应编码*/ private int code; /**响应消息*/ private String msg; /**数据总量*/ private int count; /**数据*/ private Object data; public R() { } public R(int code, String msg, int count, Object data) { super(); this.code = code; this.msg = msg; this.count = count; this.data = data; } @Override public String toString() { return "R [code=" + code + ", msg=" + msg + ", count=" + count + ", data=" + data + "]"; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
2.3.2、数据访问UserDao.java
package com.zhangguo.dao; import java.util.ArrayList; import java.util.List; import java.util.UUID; import com.zhangguo.entity.User; /** * 用户数据访问 */ public class UserDao { private static List<User> users = new ArrayList<>(); static { for (int i = 1; i <= 500; i += 3) { users.add(new User(i, "张国立" + UUID.randomUUID(), "中国北京" + UUID.randomUUID())); users.add(new User(i + 1, "张学友" + UUID.randomUUID(), "中国香港" + UUID.randomUUID())); users.add(new User(i + 2, "张慧妹" + UUID.randomUUID(), "中国珠海" + UUID.randomUUID())); } } //http://www.layui.com/demo/table/user/?page=1&limit=10 /** 获得所有用户 */ public List<User> getPager(int page,int limit) { List<User> list = new ArrayList<>(); int start=(page-1)*limit; for (int i =start; i <start+limit&&i<users.size(); i++) { list.add(users.get(i)); } return list; } /** 获得所有用户 */ public List<User> getAllUsers() { return users; } /** 添加用户 */ public void addUser(User user) { if (user.getId() <= 0) { // 未设置id int index = users.size() - 1; // 获得最后一个用户的索引号 if (index < 0) { // 如没有一个用户 user.setId(1); // 编号为1 } else { user.setId(users.get(index).getId() + 1); // 获得最后一个用户的编号+1 } } users.add(user); } /** 删除用户 */ public void delUser(int id) { User delUser = null; for (User user : users) { if (user.getId() == id) { delUser = user; break; } } users.remove(delUser); } public void updateUser(User obj) { User editUser = null; for (User user : users) { if (user.getId() == obj.getId()) { editUser = user; break; } } editUser.setName(obj.getName()); editUser.setCity(obj.getCity()); } }
2.3.3、分页服务
package com.zhangguo.action; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.zhangguo.dao.UserDao; import com.zhangguo.entity.User; import com.zhangguo.test.JsonUtils; import com.zhangguo.utils.R; /** * 用户控制器 */ @WebServlet("/UserController") public class UserController extends HttpServlet { private static final long serialVersionUID = 1L; private UserDao userDao = new UserDao(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); response.setContentType("application/json;charset=utf-8"); request.setCharacterEncoding("utf-8"); String action = request.getParameter("action"); if (action.equals("list")) { list(response); //获得所有用户 }else if (action.equals("add")) { add(request, response); //添加新用户 } else if (action.equals("del")) { //删除用户 delUser(request, response); } else if (action.equals("update")) { //更新用户 int id =Integer.parseInt(request.getParameter("id")); String name = request.getParameter("name"); String city = request.getParameter("city"); userDao.updateUser(new User(id,name, city)); delay(); response.getWriter().print("{\"msg\":\"更新成功\"}"); } else if (action.equals("pager")) { //分页 int page =Integer.parseInt(request.getParameter("page")); int limit =Integer.parseInt(request.getParameter("limit")); R r=new R(); r.setCode(0); r.setMsg("获得数据成功"); r.setCount(500); r.setData(userDao.getPager(page, limit)); delay(); response.getWriter().print(JsonUtils.toJson(r)); } } /**删除用户*/ public void delUser(HttpServletRequest request, HttpServletResponse response) throws IOException { int id =Integer.parseInt(request.getParameter("id")); userDao.delUser(id); delay(); response.getWriter().print("{\"msg\":\"删除成功\"}"); } /**添加新用户*/ public void add(HttpServletRequest request, HttpServletResponse response) throws IOException { String name = request.getParameter("name"); String city = request.getParameter("city"); userDao.addUser(new User(name, city)); delay(); response.getWriter().print("{\"msg\":\"添加成功\"}"); } /**获得所有用户*/ public void list(HttpServletResponse response) throws IOException { //Java 对象 - > 字符串 序列化成JSON //字符串 -> Java对象 反序列化 String result = "["; for (User user : userDao.getAllUsers()) { result += "{\"id\":" + user.getId() + ",\"name\":\"" + user.getName() + "\",\"city\":\"" + user.getCity() + "\"},"; } if (result.substring(result.length() - 1, result.length()).equals(",")) { result = result.substring(0, result.length() - 1); } result += "]"; delay(); response.getWriter().print(result); } public void delay(){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
2.3.4、服务测试
2.3.5、对接前后台
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>layout 后台大布局 - Layui</title> <link rel="stylesheet" href="js/layui/css/layui.css"> </head> <body class="layui-layout-body"> <div class="layui-layout layui-layout-admin"> <div class="layui-header"> <div class="layui-logo">layui 后台布局</div> <!-- 头部区域(可配合layui已有的水平导航) --> <ul class="layui-nav layui-layout-left"> <li class="layui-nav-item"> <a href="">控制台</a> </li> <li class="layui-nav-item"> <a href="">商品管理</a> </li> <li class="layui-nav-item"> <a href="">用户</a> </li> <li class="layui-nav-item"> <a href="javascript:;">其它系统</a> <dl class="layui-nav-child"> <dd> <a href="">邮件管理</a> </dd> <dd> <a href="">消息管理</a> </dd> <dd> <a href="">授权管理</a> </dd> </dl> </li> </ul> <ul class="layui-nav layui-layout-right"> <li class="layui-nav-item"> <a href="javascript:;"> <img src="http://t.cn/RCzsdCq" class="layui-nav-img"> 贤心 </a> <dl class="layui-nav-child"> <dd> <a href="">基本资料</a> </dd> <dd> <a href="">安全设置</a> </dd> </dl> </li> <li class="layui-nav-item"> <a href="">退了</a> </li> </ul> </div> <div class="layui-side layui-bg-black"> <div class="layui-side-scroll"> <!-- 左侧导航区域(可配合layui已有的垂直导航) --> <ul class="layui-nav layui-nav-tree" lay-filter="test"> <li class="layui-nav-item layui-nav-itemed"> <a class="" href="javascript:;">所有商品</a> <dl class="layui-nav-child"> <dd> <a href="javascript:;">列表一</a> </dd> <dd> <a href="javascript:;">列表二</a> </dd> <dd> <a href="javascript:;">列表三</a> </dd> <dd> <a href="http://www.baidu.com">超链接</a> </dd> </dl> </li> <li class="layui-nav-item"> <a href="javascript:;">解决方案</a> <dl class="layui-nav-child"> <dd> <a href="javascript:;">列表一</a> </dd> <dd> <a href="javascript:;">列表二</a> </dd> <dd> <a href="">超链接</a> </dd> </dl> </li> <li class="layui-nav-item"> <a href="">云市场</a> </li> <li class="layui-nav-item"> <a href="">发布商品</a> </li> </ul> </div> </div> <div class="layui-body"> <!-- 内容主体区域 --> <div style="padding: 15px;"> <table id="users" lay-filter="test"></table> </div> </div> <div class="layui-footer"> <!-- 底部固定区域 --> © layui.com - 底部固定区域 </div> </div> <script src="js/layui/layui.all.js"></script> <script> //JavaScript代码区域 layui.use('element', function() { var element = layui.element; }); layui.use('table', function(){ var table = layui.table; //第一个实例 table.render({ elem: '#users' ,height: 515 ,url: 'UserController?action=pager' //数据接口 ,page: true //开启分页 ,cols: [[ //表头 {field: 'id', title: 'ID', width:80, sort: true, fixed: 'left'} ,{field: 'name', title: '用户名', width:300,sort: true} ,{field: 'city', title: '城市', width:300,sort: true} ,{field: 'birthday', title: '出生日期', width: 177,sort: true} ]] }); }); </script> </body> </html>
2.3.6、运行结果
三、综合示例
3.1、概要
实现商品的后台管理功能,添加,修改,删除,更新,上传,富文本,前台展示,手机端的浏览
3.2、创建数据库
--商品 --编号,名称,价格,上架时间,状态,图片 --创建数据库 create database GoMallPro; use GoMallPro; --创建表 create table Product ( id int primary key identity(100000,1), name nvarchar(256) not null, price decimal(9,2), addDate datetime, [state] int default(1), picture varchar(126) ) --添加数据 INSERT INTO [GoMallPro].[dbo].[Product] ([name] ,[price] ,[addDate] ,[state] ,[picture]) select 'iPhone X',5898.5,GETDATE(),1,'pic(1).jpg' union select 'Meizu 魅蓝1',999.9,'2014-01-12',1,'pic(2).jpg' union select 'ZTE U880',566.85,GETDATE(),0,'pic(3).jpg' union select '华为 荣耀6',1487.3,'2018-04-15',1,'pic(4).jpg' union select '小米 Max 2',1398.2,'2017-12-09',0,'pic(5).jpg' SELECT [id] ,[name],[price] ,[addDate],[state],[picture] FROM [Product]
结果:
3.3、创建项目
创建一个web项目,记得选择生成web.xml文件
创建完成
在webcontent目录下添加images图片目录
在webcontent目录下添加layui 后台模板
添加后的效果
部署运行
创建页,未修改的模板
后台管理
3.4、创建实体类
Product.java
package com.zhangguo.gomallpro.model; import java.math.BigDecimal; import java.util.Date; /** * 产品 Bean * */ public class Product { private int id; private String name; private BigDecimal price; private Date addDate; private int state; private String picture; public Product() { } public Product(int id, String name, BigDecimal price, Date addDate, int state, String picture) { super(); this.id = id; this.name = name; this.price = price; this.addDate = addDate; this.state = state; this.picture = picture; } @Override public String toString() { return "Product [id=" + id + ", name=" + name + ", price=" + price + ", addDate=" + addDate + ", state=" + state + ", picture=" + picture + "]"; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } public Date getAddDate() { return addDate; } public void setAddDate(Date addDate) { this.addDate = addDate; } public int getState() { return state; } public void setState(int state) { this.state = state; } public String getPicture() { return picture; } public void setPicture(String picture) { this.picture = picture; } }
3.5、数据访问
ProductDao.java
先添加依赖包sqljdbc4.jar
JDBCUitls工具类
package com.zhangguo.gomallpro.utils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class JDBCUtils { public static String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; public static String URL = "jdbc:sqlserver://localhost:1433;databasename=GoMallPro"; public static String USER_NAME = "sa"; public static String PASSWORD = "sa"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } private JDBCUtils() { } /** * Get connection * * @return */ public static Connection getconnnection() { Connection con = null; try { con = DriverManager.getConnection(URL, USER_NAME, PASSWORD); } catch (SQLException e) { e.printStackTrace(); } return con; } /** * Close connection * * @param rs * @param st * @param con */ public static void close(ResultSet rs, Statement st, Connection con) { try { try { if (rs != null) { rs.close(); } } finally { try { if (st != null) { st.close(); } } finally { if (con != null) con.close(); } } } catch (SQLException e) { e.printStackTrace(); } } /** * Close connection * * @param rs */ public static void close(ResultSet rs) { Statement st = null; Connection con = null; try { try { if (rs != null) { st = rs.getStatement(); rs.close(); } } finally { try { if (st != null) { con = st.getConnection(); st.close(); } } finally { if (con != null) { con.close(); } } } } catch (SQLException e) { e.printStackTrace(); } } /** * Close connection * * @param st * @param con */ public static void close(Statement st, Connection con) { try { try { if (st != null) { st.close(); } } finally { if (con != null) con.close(); } } catch (SQLException e) { e.printStackTrace(); } } /** * insert/update/delete * * @param sql * @param args * @return */ public static int update(String sql, Object... args) { int result = 0; Connection con = getconnnection(); PreparedStatement ps = null; try { ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } result = ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { close(ps, con); } return result; } /** * query, because need to manually close the resource, so not recommended * for use it * * @param sql * @param args * @return ResultSet */ @Deprecated public static ResultSet query(String sql, Object... args) { ResultSet result = null; Connection con = getconnnection(); PreparedStatement ps = null; try { ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } result = ps.executeQuery(); } catch (SQLException e) { e.printStackTrace(); } return result; } /** * Query a single record * * @param sql * @param args * @return Map<String,Object> */ public static Map<String, Object> queryForMap(String sql, Object... args) { Map<String, Object> result = new HashMap<String, Object>(); List<Map<String, Object>> list = queryForList(sql, args); if (list.size() > 0) { result = list.get(0); } return result; } /** * Query a single record * * @param sql * @param args * @return <T> */ public static <T> T queryForObject(String sql, Class<T> clz, Object... args) { T result = null; List<T> list = queryForList(sql, clz, args); if (list.size() > 0) { result = list.get(0); } return result; } /** * Query a single record * * @param sql * @param args * @return List<Map<String,Object>> */ public static List<Map<String, Object>> queryForList(String sql, Object... args) { List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(); Connection con = null; ResultSet rs = null; PreparedStatement ps = null; try { con = getconnnection(); ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } rs = ps.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); while (rs.next()) { Map<String, Object> map = new HashMap<String, Object>(); for (int i = 1; i <= columnCount; i++) { map.put(rsmd.getColumnLabel(i), rs.getObject(i)); } result.add(map); } } catch (SQLException e) { e.printStackTrace(); } finally { close(rs, ps, con); } return result; } /** * Query a single record * * @param sql * @param args * @return List<T> */ public static <T> List<T> queryForList(String sql, Class<T> clz, Object... args) { List<T> result = new ArrayList<T>(); Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { con = getconnnection(); ps = con.prepareStatement(sql); if (args != null) { for (int i = 0; i < args.length; i++) { ps.setObject((i + 1), args[i]); } } rs = ps.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); while (rs.next()) { T obj = clz.newInstance(); for (int i = 1; i <= columnCount; i++) { String columnName = rsmd.getColumnName(i); String methodName = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1, columnName.length()); Method method[] = clz.getMethods(); for (Method meth : method) { if (methodName.equals(meth.getName())) { meth.invoke(obj, rs.getObject(i)); } } } result.add(obj); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } finally { close(rs, ps, con); } return result; } }
ProductDao.java
package com.zhangguo.gomallpro.dao; import java.util.List; import com.zhangguo.gomallpro.model.Product; import com.zhangguo.gomallpro.utils.JDBCUtils; public class ProductDao { public List<Product> getPager(int page, int limit) { // 开始索引 int start = (page - 1) * limit + 1; // 结束索引 int end = page * limit; String sql = "select * from(SELECT [id] ,[name],[price] ,[addDate],[state],[picture],ROW_NUMBER() over(order by id desc) row FROM [Product]) t where t.row>=? and t.row<=?"; //执行查询返回List<Product> return JDBCUtils.queryForList(sql, Product.class, start,end); } public static void main(String[] args) { ProductDao dao=new ProductDao(); for (Product p : dao.getPager(1, 10)) { System.out.println(p); } } }
测试结果:
decimal->BigDecimal
3.6、产品展示
ProductDao.java
package com.zhangguo.gomallpro.dao; import java.util.List; import java.util.Map; import com.zhangguo.gomallpro.model.Product; import com.zhangguo.gomallpro.utils.JDBCUtils; public class ProductDao { /**产品分页列表*/ public List<Product> getPager(int page, int limit) { // 开始索引 int start = (page - 1) * limit + 1; // 结束索引 int end = page * limit; String sql = "select * from(SELECT [id] ,[name],[price] ,[addDate],[state],[picture],ROW_NUMBER() over(order by id desc) row FROM [Product]) t where t.row>=? and t.row<=?"; //执行查询返回List<Product> return JDBCUtils.queryForList(sql, Product.class, start,end); } /**获得总记录数*/ public int getCount() { String sql="select COUNT(*) count from Product"; Map<String,Object> map=JDBCUtils.queryForMap(sql); return (int)map.get("count"); } //JUnit public static void main(String[] args) { ProductDao dao=new ProductDao(); System.out.println(dao.getCount()); for (Product p : dao.getPager(2, 10)) { System.out.println(p); } } }
测试:
分页工具类R.java
package com.zhangguo.gomallpro.utils; public class R { /**响应编码*/ private int code; /**响应消息*/ private String msg; /**数据总量*/ private int count; /**数据*/ private Object data; public String toJson(){ return JsonUtils.toJson(this); } public R() { } public static R ok(){ return ok(0,null); } public static R ok(int count, Object data){ return new R(0, "操作成功!", count, data); } public static R ok(String msg){ return new R(0,msg, 0,null); } public static R error(int count, Object data){ return new R(1, "操作失败!", count, data); } public static R error(){ return error(0,null); } public static R error(String msg){ return new R(1, msg,0,null); } public R(int code, String msg, int count, Object data) { super(); this.code = code; this.msg = msg; this.count = count; this.data = data; } @Override public String toString() { return "R [code=" + code + ", msg=" + msg + ", count=" + count + ", data=" + data + "]"; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
JsonUtil工具类
package com.zhangguo.gomallpro.utils; import java.text.SimpleDateFormat; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public class JsonUtils { /** * 序列化成json * */ public static String toJson(Object obj) { // 对象映射器 ObjectMapper mapper = new ObjectMapper(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd HH:mm:ss"); mapper.setDateFormat(sdf); String result = null; // 序列化user对象为json字符串 try { result = mapper.writeValueAsString(obj); } catch (JsonProcessingException e) { e.printStackTrace(); } return result; } /** * 反序列化成对象 * */ public static <T> T toObject(String json,Class<T> valueType) { //对象映射器 ObjectMapper mapper=new ObjectMapper(); T result=null; try { result=mapper.readValue(json,valueType); }catch (Exception e) { e.printStackTrace(); } return result; } }
产品控制器,ProductController.java
package com.zhangguo.gomallpro.action; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.zhangguo.gomallpro.dao.ProductDao; import com.zhangguo.gomallpro.utils.R; /** * 产品控制器 */ @WebServlet("/ProductController") public class ProductController extends HttpServlet { private static final long serialVersionUID = 1L; PrintWriter out; ProductDao dao=new ProductDao(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码 response.setCharacterEncoding("utf-8"); request.setCharacterEncoding("utf-8"); response.setContentType("application/json;charset=utf-8"); out=response.getWriter(); String action=request.getParameter("action"); if(action.equals("getPager")){ int page=Integer.parseInt(request.getParameter("page")); int limit=Integer.parseInt(request.getParameter("limit")); out.print(R.ok(dao.getCount(), dao.getPager(page, limit)).toJson()); }else{ out.print(R.error("不存在的动作")); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
测试服务
product.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>商品管理</title> <link rel="stylesheet" href="./plugins/layui/css/layui.css" media="all"> </head> <body> <div style="padding: 10px 0 0 10px;"> <button class="layui-btn layui-btn-sm layui-btn-normal" id="btnAdd" data-type="auto">添加</button> </div> <table id="productList" lay-filter="test"></table> <script src="./plugins/layui/layui.js"></script> <script> layui.use('table', function() { var table = layui.table; //第一个实例 table.render({ elem: '#productList' ,height:450 ,url: 'ProductController?action=getPager' //数据接口 ,page: true //开启分页 ,cols: [[ //表头 {field: 'id', title: '编号', width:130, sort: true, fixed: 'left'} ,{field: 'name', title: '商品名称', width:200} ,{field: 'price', title: '价格', width:100, sort: true} ,{field: 'picture', title: '图片', width:130} ,{field: 'addDate', title: '上架时间', width: 160, sort: true} ,{field: 'state', title: '状态', width: 80} ]] }); }); layui.use('jquery',function(){ var $=layui.jquery; $("#btnAdd").click(function() { //通过这种方式弹出的层,每当它被选择,就会置顶。 layer.open({ type: 1, shade: [0.4, '#393D49'], area: ['600px', '400px'], maxmin: true, content: $("#form1"), zIndex: layer.zIndex, //重点1 success: function(layero) { layer.setTop(layero); //重点2 }, btn: ['按钮一', '按钮二', '按钮三'], yes: function(index, layero) { //按钮【按钮一】的回调 }, btn2: function(index, layero) { //按钮【按钮二】的回调 //return false 开启该代码可禁止点击该按钮关闭 }, btn3: function(index, layero) { //按钮【按钮三】的回调 //return false 开启该代码可禁止点击该按钮关闭 }, cancel: function() { //右上角关闭回调 //return false 开启该代码可禁止点击该按钮关闭 } }); }); }); </script> </body> <fieldset id="form1" hidden="hidden"> <legend>用户信息</legend> <p> <label for="name">姓名:</label> <input type="text" name="name" id="name" placeholder="请输入姓名" /> <span id="nameMsg" class="red"></span> </p> <p> <label for="city">城市:</label> <input type="text" name="city" id="city" placeholder="请输入城市" /> <span id="cityMsg" class="red"></span> </p> <p> <button type="button" id="btnSave" onclick="layer.closeAll()">保存</button> <button type="button" id="btnUpdate">更新</button> </p> </fieldset> </html>
运行结果
3.7、AJAX文件异步上传
3.7.1、前端页面
html
<div class="layui-upload"> <button type="button" class="layui-btn" id="test1">上传图片</button> <div class="layui-upload-list"> <img class="layui-upload-img" id="demo1"> <p id="demoText"></p> </div> </div>
js
layui.use('upload', function(){ var $ = layui.jquery ,upload = layui.upload; //普通图片上传 var uploadInst = upload.render({ elem: '#test1' ,url: 'UploadFile' ,before: function(obj){ //预读本地文件示例,不支持ie8 obj.preview(function(index, file, result){ $('#demo1').attr('src', result); //图片链接(base64) }); } ,done: function(res){ //如果上传失败 if(res.code > 0){ return layer.msg('上传失败'); } //上传成功 layer.msg(res.msg); } ,error: function(){ //演示失败状态,并实现重传 var demoText = $('#demoText'); demoText.html('<span style="color: #FF5722;">上传失败</span> <a class="layui-btn layui-btn-mini demo-reload">重试</a>'); demoText.find('.demo-reload').on('click', function(){ uploadInst.upload(); }); } }); });
3.7.2、后台服务
添加jar包的依赖
定义上传文件的Servlet
package com.zhangguo.gomallpro.action; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import com.zhangguo.gomallpro.utils.R; @SuppressWarnings("serial") @WebServlet("/UploadFile") public class UploadFile extends HttpServlet { @SuppressWarnings("unchecked") public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置编码 response.setCharacterEncoding("utf-8"); // 物理路径 String savePath = this.getServletConfig().getServletContext().getRealPath(""); savePath = savePath + "images\\"; File f1 = new File(savePath); if (!f1.exists()) { f1.mkdirs(); } DiskFileItemFactory fac = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(fac); upload.setHeaderEncoding("utf-8"); List<FileItem> fileList = null; try { fileList = upload.parseRequest(request); } catch (FileUploadException ex) { return; } Iterator<FileItem> it = fileList.iterator(); String name = ""; String extName = ""; while (it.hasNext()) { FileItem item = it.next(); if (!item.isFormField()) { name = item.getName(); long size = item.getSize(); String type = item.getContentType(); System.out.println(size + " " + type); if (name == null || name.trim().equals("")) { continue; } // 扩展名格式: if (name.lastIndexOf(".") >= 0) { extName = name.substring(name.lastIndexOf(".")); } File file = null; do { // 生成文件名: name = UUID.randomUUID().toString(); file = new File(savePath + name + extName); } while (file.exists()); File saveFile = new File(savePath + name + extName); try { item.write(saveFile); } catch (Exception exp) { response.getWriter().write(exp.getMessage()); exp.printStackTrace(); } } } R r = new R(); r.setCode(0); r.setMsg("上传成功"); Map<String, String> data = new HashMap<String, String>(); data.put("src", "images/" + name + extName); data.put("name",name + extName); r.setData(data); response.getWriter().print(r.toJson()); } }
3.7.3、演示结果
3.7.4、其它上传组件
uploadify
3.8、富文本编辑器Kindeditor
KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE、Firefox、Chrome、Safari、Opera等主流浏览器。KindEditor使用JavaScript编写,可以无缝的于Java、.NET、PHP、ASP等程序接合。 KindEditor非常适合在CMS、商城、论坛、博客、Wiki、电子邮件等互联网应用上使用
1. 体积小,加载速度快,但功能十分丰富。2. 内置自定义range,完美地支持span标记。
3. 基于插件的方式设计,所有功能都是插件,增加自定义和扩展功能非常简单。
4. 修改编辑器风格很容易,只需修改一个CSS文件。
5. 支持大部分主流浏览器,比如IE、Firefox、Safari、Chrome、Opera。
Github: https://github.com/kindsoft/kindeditor
Oschina: http://git.oschina.net/luolonghao/kindeditor
下载地址:https://github.com/kindsoft/kindeditor/releases/download/v4.1.11/kindeditor-4.1.11-zh-CN.zip
3.8.1、使用Kindeditor
1. 下载编辑器
下载 KindEditor 最新版本,下载之后打开 examples/index.html 就可以看到演示。
下载页面: http://www.kindsoft.net/down.php
2. 部署编辑器
解压 kindeditor-x.x.x.zip 文件,将所有文件上传到您的网站程序目录里,例如:http://您的域名/editor/
Note:
您可以根据需求删除以下目录后上传到服务器。
asp - ASP程序
asp.net - ASP.NET程序
php - PHP程序
jsp - JSP程序
examples - 演示文件
3. 修改HTML页面
1.在需要显示编辑器的位置添加textarea输入框。
<textarea id="editor_id" name="content" style="width:700px;height:300px;">
<strong>HTML内容</strong>
</textarea>
Note:
id在当前页面必须是唯一的值。
在textarea里设置HTML内容即可实现编辑,在这里需要注意的是,如果从服务器端程序(ASP、PHP、ASP.NET等)直接显示内容,则必须转换HTML特殊字符(>,<,&,”)。具体请参考各语言目录下面的demo.xxx程序,目前支持ASP、ASP.NET、PHP、JSP。
在有些浏览器上不设宽度和高度可能显示有问题,所以最好设一下宽度和高度。宽度和高度可用inline样式设置,也可用 编辑器初始化参数 设置。
2.在该HTML页面添加以下脚本。
<script charset="utf-8" src="/editor/kindeditor.js"></script>
<script charset="utf-8" src="/editor/lang/zh_CN.js"></script>
<script>
var editor;
KindEditor.ready(function(K) {
editor = K.create('#editor_id');
});
</script>
Note:
第一个参数可用其它CSS选择器,匹配多个textarea时只在第一个元素上加载编辑器。
通过K.create函数的第二个参数,可以对编辑器进行配置,具体参数请参考 编辑器初始化参数 。
var options = {
cssPath : '/css/index.css',
filterMode : true
};
var editor = K.create('textarea[name="content"]', options);
4. 获取HTML数据
// 取得HTML内容
html = editor.html();
// 同步数据后可以直接取得textarea的value
editor.sync();
html = document.getElementById('editor_id').value; // 原生API
html = K('#editor_id').val(); // KindEditor Node API
html = $('#editor_id').val(); // jQuery
// 设置HTML内容
editor.html('HTML内容');
Note:
KindEditor的可视化操作在新创建的iframe上执行,代码模式下的textarea框也是新创建的,所以最后提交前需要执行 sync() 将HTML数据设置到原来的textarea。
KindEditor在默认情况下自动寻找textarea所属的form元素,找到form后onsubmit事件里添加sync函数,所以用form方式提交数据,不需要手动执行sync()函数。
3.8.2、文件上传
将依赖的jar文件放置到项目的lib目录中,3个jar文件已在kindeditor中存在
找到用于处理上传的jsp程序,根据需要修改
upload_json.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.*,java.io.*" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="org.apache.commons.fileupload.*" %> <%@ page import="org.apache.commons.fileupload.disk.*" %> <%@ page import="org.apache.commons.fileupload.servlet.*" %> <%@ page import="org.json.simple.*" %> <% /** * KindEditor JSP * * 本JSP程序是演示程序,建议不要直接在实际项目中使用。 * 如果您确定直接使用本程序,使用之前请仔细确认相关安全设置。 * */ //文件保存目录路径 String savePath = pageContext.getServletContext().getRealPath("/") + "attached/"; //文件保存目录URL String saveUrl = request.getContextPath() + "/attached/"; //定义允许上传的文件扩展名 HashMap<String, String> extMap = new HashMap<String, String>(); extMap.put("image", "gif,jpg,jpeg,png,bmp"); extMap.put("flash", "swf,flv"); extMap.put("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb"); extMap.put("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2"); //最大文件大小 long maxSize = 1000000; response.setContentType("text/html; charset=UTF-8"); if(!ServletFileUpload.isMultipartContent(request)){ out.println(getError("请选择文件。")); return; } //检查目录 File uploadDir = new File(savePath); if(!uploadDir.isDirectory()){ out.println(getError("上传目录不存在。")); return; } //检查目录写权限 if(!uploadDir.canWrite()){ out.println(getError("上传目录没有写权限。")); return; } String dirName = request.getParameter("dir"); if (dirName == null) { dirName = "image"; } if(!extMap.containsKey(dirName)){ out.println(getError("目录名不正确。")); return; } //创建文件夹 savePath += dirName + "/"; saveUrl += dirName + "/"; File saveDirFile = new File(savePath); if (!saveDirFile.exists()) { saveDirFile.mkdirs(); } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String ymd = sdf.format(new Date()); savePath += ymd + "/"; saveUrl += ymd + "/"; File dirFile = new File(savePath); if (!dirFile.exists()) { dirFile.mkdirs(); } FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); upload.setHeaderEncoding("UTF-8"); List items = upload.parseRequest(request); Iterator itr = items.iterator(); while (itr.hasNext()) { FileItem item = (FileItem) itr.next(); String fileName = item.getName(); long fileSize = item.getSize(); if (!item.isFormField()) { //检查文件大小 if(item.getSize() > maxSize){ out.println(getError("上传文件大小超过限制。")); return; } //检查扩展名 String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); if(!Arrays.<String>asList(extMap.get(dirName).split(",")).contains(fileExt)){ out.println(getError("上传文件扩展名是不允许的扩展名。\n只允许" + extMap.get(dirName) + "格式。")); return; } SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); String newFileName = df.format(new Date()) + "_" + new Random().nextInt(1000) + "." + fileExt; try{ File uploadedFile = new File(savePath, newFileName); item.write(uploadedFile); }catch(Exception e){ out.println(getError("上传文件失败。")); return; } JSONObject obj = new JSONObject(); obj.put("error", 0); obj.put("url", saveUrl + newFileName); out.println(obj.toJSONString()); } } %> <%! private String getError(String message) { JSONObject obj = new JSONObject(); obj.put("error", 1); obj.put("message", message); return obj.toJSONString(); } %>
文件管理file_manager_json.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.util.*,java.io.*" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page import="org.json.simple.*" %> <% /** * KindEditor JSP * * 本JSP程序是演示程序,建议不要直接在实际项目中使用。 * 如果您确定直接使用本程序,使用之前请仔细确认相关安全设置。 * */ //根目录路径,可以指定绝对路径,比如 /var/www/attached/ String rootPath = pageContext.getServletContext().getRealPath("/") + "attached/"; //根目录URL,可以指定绝对路径,比如 http://www.yoursite.com/attached/ String rootUrl = request.getContextPath() + "/attached/"; //图片扩展名 String[] fileTypes = new String[]{"gif", "jpg", "jpeg", "png", "bmp"}; String dirName = request.getParameter("dir"); if (dirName != null) { if(!Arrays.<String>asList(new String[]{"image", "flash", "media", "file"}).contains(dirName)){ out.println("Invalid Directory name."); return; } rootPath += dirName + "/"; rootUrl += dirName + "/"; File saveDirFile = new File(rootPath); if (!saveDirFile.exists()) { saveDirFile.mkdirs(); } } //根据path参数,设置各路径和URL String path = request.getParameter("path") != null ? request.getParameter("path") : ""; String currentPath = rootPath + path; String currentUrl = rootUrl + path; String currentDirPath = path; String moveupDirPath = ""; if (!"".equals(path)) { String str = currentDirPath.substring(0, currentDirPath.length() - 1); moveupDirPath = str.lastIndexOf("/") >= 0 ? str.substring(0, str.lastIndexOf("/") + 1) : ""; } //排序形式,name or size or type String order = request.getParameter("order") != null ? request.getParameter("order").toLowerCase() : "name"; //不允许使用..移动到上一级目录 if (path.indexOf("..") >= 0) { out.println("Access is not allowed."); return; } //最后一个字符不是/ if (!"".equals(path) && !path.endsWith("/")) { out.println("Parameter is not valid."); return; } //目录不存在或不是目录 File currentPathFile = new File(currentPath); if(!currentPathFile.isDirectory()){ out.println("Directory does not exist."); return; } //遍历目录取的文件信息 List<Hashtable> fileList = new ArrayList<Hashtable>(); if(currentPathFile.listFiles() != null) { for (File file : currentPathFile.listFiles()) { Hashtable<String, Object> hash = new Hashtable<String, Object>(); String fileName = file.getName(); if(file.isDirectory()) { hash.put("is_dir", true); hash.put("has_file", (file.listFiles() != null)); hash.put("filesize", 0L); hash.put("is_photo", false); hash.put("filetype", ""); } else if(file.isFile()){ String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); hash.put("is_dir", false); hash.put("has_file", false); hash.put("filesize", file.length()); hash.put("is_photo", Arrays.<String>asList(fileTypes).contains(fileExt)); hash.put("filetype", fileExt); } hash.put("filename", fileName); hash.put("datetime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file.lastModified())); fileList.add(hash); } } if ("size".equals(order)) { Collections.sort(fileList, new SizeComparator()); } else if ("type".equals(order)) { Collections.sort(fileList, new TypeComparator()); } else { Collections.sort(fileList, new NameComparator()); } JSONObject result = new JSONObject(); result.put("moveup_dir_path", moveupDirPath); result.put("current_dir_path", currentDirPath); result.put("current_url", currentUrl); result.put("total_count", fileList.size()); result.put("file_list", fileList); response.setContentType("application/json; charset=UTF-8"); out.println(result.toJSONString()); %> <%! public class NameComparator implements Comparator { public int compare(Object a, Object b) { Hashtable hashA = (Hashtable)a; Hashtable hashB = (Hashtable)b; if (((Boolean)hashA.get("is_dir")) && !((Boolean)hashB.get("is_dir"))) { return -1; } else if (!((Boolean)hashA.get("is_dir")) && ((Boolean)hashB.get("is_dir"))) { return 1; } else { return ((String)hashA.get("filename")).compareTo((String)hashB.get("filename")); } } } public class SizeComparator implements Comparator { public int compare(Object a, Object b) { Hashtable hashA = (Hashtable)a; Hashtable hashB = (Hashtable)b; if (((Boolean)hashA.get("is_dir")) && !((Boolean)hashB.get("is_dir"))) { return -1; } else if (!((Boolean)hashA.get("is_dir")) && ((Boolean)hashB.get("is_dir"))) { return 1; } else { if (((Long)hashA.get("filesize")) > ((Long)hashB.get("filesize"))) { return 1; } else if (((Long)hashA.get("filesize")) < ((Long)hashB.get("filesize"))) { return -1; } else { return 0; } } } } public class TypeComparator implements Comparator { public int compare(Object a, Object b) { Hashtable hashA = (Hashtable)a; Hashtable hashB = (Hashtable)b; if (((Boolean)hashA.get("is_dir")) && !((Boolean)hashB.get("is_dir"))) { return -1; } else if (!((Boolean)hashA.get("is_dir")) && ((Boolean)hashB.get("is_dir"))) { return 1; } else { return ((String)hashA.get("filetype")).compareTo((String)hashB.get("filetype")); } } } %>
KindEditor默认提供ASP、ASP.NET、PHP、JSP上传程序,这些程序是演示程序,建议不要直接在实际项目中使用。如果您确定直接使用本程序,使用之前请仔细确认相关安全设置。
选择程序语言?
// ASP
KindEditor.ready(function(K) {
K.create('#textarea_id', {
uploadJson : '../asp/upload_json.asp',
fileManagerJson : '../asp/file_manager_json.asp',
allowFileManager : true
});
});
// ASP.NET
KindEditor.ready(function(K) {
K.create('#textarea_id', {
uploadJson : '../asp.net/upload_json.ashx',
fileManagerJson : '../asp.net/file_manager_json.ashx',
allowFileManager : true
});
});
// JSP
KindEditor.ready(function(K) {
K.create('#textarea_id', {
uploadJson : '../jsp/upload_json.jsp',
fileManagerJson : '../jsp/file_manager_json.jsp',
allowFileManager : true
});
});
Note:
具体使用方法请参见各语言(asp、asp.net、php、jsp)目录下的demo.xxx文件。
完整示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>kind editor Demo</title> </head> <body> <h2>富文本编辑器</h2> <div> 商品详细: <textarea cols="80" rows="10" id="details" style="width:700px"></textarea> <button type="button" id="btnSubmit">提交</button> </div> <div id="divContent"></div> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="text/javascript" src="js/kindeditor4111/kindeditor-all-min.js"></script> <script type="text/javascript" src="js/kindeditor4111/lang/zh-CN.js"></script> <script> var editor; KindEditor.ready(function(K) { editor = K.create('#details',{ uploadJson : 'js/kindeditor4111/jsp/upload_json.jsp', //上传程序 fileManagerJson : 'js/kindeditor4111/jsp/file_manager_json.jsp', //文件管理 allowFileManager : true //是否允许上传 }); //创建一个富文本编辑器 }); $("#btnSubmit").click(function(){ $("#divContent").html(editor.html()); }); </script> </body> </html>
运行结果:
3.8.3、其它属性设置
K.options = { designMode : true, fullscreenMode : false, filterMode : true, wellFormatMode : true, shadowMode : true, loadStyleMode : true, basePath : K.basePath, themesPath : K.basePath + 'themes/', langPath : K.basePath + 'lang/', pluginsPath : K.basePath + 'plugins/', themeType : 'default', langType : 'zh-CN', urlType : '', newlineTag : 'p', resizeType : 2, syncType : 'form', pasteType : 2, dialogAlignType : 'page', useContextmenu : true, fullscreenShortcut : false, bodyClass : 'ke-content', indentChar : '\t', cssPath : '', cssData : '', minWidth : 650, minHeight : 100, minChangeSize : 50, zIndex : 811213, items : [ 'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste', 'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright', 'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript', 'superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/', 'formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage', 'flash', 'media', 'insertfile', 'table', 'hr', 'emoticons', 'baidumap', 'pagebreak', 'anchor', 'link', 'unlink', '|', 'about' ], noDisableItems : ['source', 'fullscreen'], colorTable : [ ['#E53333', '#E56600', '#FF9900', '#64451D', '#DFC5A4', '#FFE500'], ['#009900', '#006600', '#99BB00', '#B8D100', '#60D978', '#00D5FF'], ['#337FE5', '#003399', '#4C33E5', '#9933E5', '#CC33E5', '#EE33EE'], ['#FFFFFF', '#CCCCCC', '#999999', '#666666', '#333333', '#000000'] ], fontSizeTable : ['9px', '10px', '12px', '14px', '16px', '18px', '24px', '32px'], htmlTags : { font : ['id', 'class', 'color', 'size', 'face', '.background-color'], span : [ 'id', 'class', '.color', '.background-color', '.font-size', '.font-family', '.background', '.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.line-height' ], div : [ 'id', 'class', 'align', '.border', '.margin', '.padding', '.text-align', '.color', '.background-color', '.font-size', '.font-family', '.font-weight', '.background', '.font-style', '.text-decoration', '.vertical-align', '.margin-left' ], table: [ 'id', 'class', 'border', 'cellspacing', 'cellpadding', 'width', 'height', 'align', 'bordercolor', '.padding', '.margin', '.border', 'bgcolor', '.text-align', '.color', '.background-color', '.font-size', '.font-family', '.font-weight', '.font-style', '.text-decoration', '.background', '.width', '.height', '.border-collapse' ], 'td,th': [ 'id', 'class', 'align', 'valign', 'width', 'height', 'colspan', 'rowspan', 'bgcolor', '.text-align', '.color', '.background-color', '.font-size', '.font-family', '.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.background', '.border' ], a : ['id', 'class', 'href', 'target', 'name'], embed : ['id', 'class', 'src', 'width', 'height', 'type', 'loop', 'autostart', 'quality', '.width', '.height', 'align', 'allowscriptaccess', 'wmode'], img : ['id', 'class', 'src', 'width', 'height', 'border', 'alt', 'title', 'align', '.width', '.height', '.border'], 'p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6' : [ 'id', 'class', 'align', '.text-align', '.color', '.background-color', '.font-size', '.font-family', '.background', '.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.text-indent', '.margin-left' ], pre : ['id', 'class'], hr : ['id', 'class', '.page-break-after'], 'br,tbody,tr,strong,b,sub,sup,em,i,u,strike,s,del' : ['id', 'class'], iframe : ['id', 'class', 'src', 'frameborder', 'width', 'height', '.width', '.height'] }, layout : '<div class="container"><div class="toolbar"></div><div class="edit"></div><div class="statusbar"></div></div>' };
kindeditor API ,kindeditor使用手册,kindeditor函数,kindeditor使用,超级大收集 变量 1. KE 唯一的全局变量,也是程序的命名空间。 数据类型:Object 2. KE.version 编辑器的版本信息。 数据类型:String 3. KE.lang 编辑器的中文信息。 数据类型:Object 4. KE.scriptPath kindeditor.js的路径。 数据类型:String 5. KE.htmlPath 编辑器的HTML页面路径。 数据类型:String 注:3.4版本已废弃。 6. KE.browser 浏览器类型和版本,分别为KE.browser.VERSION、KE.browser.IE、KE.browser.WEBKIT、 KE.browser.GECKO、KE.browser.OPERA。 数据类型:Object 注:3.4以前版本直接返回字符串,分别为"IE"、"WEBKIT"、"GECKO"、"OPERA"。 7. KE.setting 编辑器的初始化属性和其它配置。 数据类型:Object 8. KE.g 一个编辑器的变量集,包含所有编辑器属性,此外还包含以下变量,经常用KE.g[id]来表示。 例如:KE.g["content_1"].iframeDoc表示id为"content_1"的编辑器的iframe document对象。 数据类型:Object 主要变量: container: 编辑器的外部element对象。 iframe: 编辑区域的iframe对象。 iframeWin: 编辑区域的iframe window对象。 iframeDoc: 编辑区域的iframe document对象。 keSel: 当前选中信息的KE.selection对象。 keRange: 当前选中信息的KE.range对象。 sel: 当前选中信息的浏览器原生selection对象。 range: 当前选中信息的浏览器原生range对象。 layoutDiv: 编辑器弹出层的div对象。3.4版本已废弃。 hideDiv: 编辑器弹出层的parent div对象。 dialog: 弹出窗口的iframe对象。3.4版本已废弃。 yesButton: 弹出窗口的确定按钮input对象。 noButton: 弹出窗口的取消按钮input对象。 previewButton: 弹出窗口的预览按钮input对象。 maskDiv: 弹出窗口时灰色遮罩层的div对象。 undoStack: undo/redo的undo记录。 redoStack: undo/redo的redo记录。 9. KE.plugin 定义编辑器的插件。 数据类型:Object 函数 1. KE.$(id, doc) 取得element对象,doc.getElementById的别名。 参数: id:String,element的id doc:Object,element所在document对象,是可选参数,默认值为document。 返回值: Object,element对象 2. KE.$$(name, doc) 创建element对象,doc.createElement的别名。 参数: name:String,element的tag name doc:Object,element所在document对象,是可选参数,默认值为document。 返回值: Object,element对象 3. KE.event.add(el, event, listener) 添加一个事件。 参数: el:Object,要添加事件的element对象 event:String,事件名称,可设置"click","change","mousedown"等。 listener:Function,事件处理回调函数。 返回值:无 4. KE.event.remove(el, event, listener) 删除已添加的一个事件。 参数: el:Object,要添加事件的element对象 event:String,事件名称,可设置"click","change","mousedown"等。 listener:Function,事件处理回调函数。 返回值:无 5. KE.event.input(el, func) 添加一个编辑器输入事件。 参数: el:Object,要添加事件的element对象 func:Function,编辑器输入内容时调用这个函数。 返回值:无 6. KE.event.ctrl(el, key, func) 添加一个Ctrl+[]事件。 参数: el:Object,要添加事件的element对象 key:String,Ctrl组合键的字母,支持A到Z。 func:Function,按下Ctrl+[]时调用这个函数。 返回值:无 7. KE.event.ready(func) 添加一个document的DOMContentLoaded事件。 参数: func:Function,DOM加载完成后调用这个函数。 返回值:无 8. KE.each(obj, func) 遍历一个object。 参数: obj:Object,要遍历的object func:Function,循环时调用这个函数,参数为object的key和value。 返回值:无 9. KE.eachNode(node, func) 遍历一个node。 参数: node:Object,要遍历的parent node func:Function,循环时调用这个函数,参数为node。 返回值:无 10. KE.format.getHtml(html, htmlTags) 把HTML转换成XHTML,当指定htmlTags参数时,按照htmlTags规则过滤HTML标签。 参数: html:String,HTML文本 htmlTags:Object,过滤规则,可选参数。 返回值: String,XHTML文本 11. KE.util.getDocumentElement() 取得document element对象。 参数:无 返回值: Object,element对象 12. KE.util.getDocumentWidth() 取得当前页面的宽度。 参数:无 返回值: Int,document宽度 13. KE.util.getDocumentHeight() 取得当前页面的高度。 参数:无 返回值: Int,document高度 14. KE.util.loadStyle(path) 在当前页面加载一个CSS文件。 参数: path:String,CSS文件的URL路径 返回值:无 15. KE.util.inArray(str, arr) 判断一个字符串是否在一个数组里。 参数: str:String arr:Array 返回值: Boolean,返回true表示在数组里,返回false表示不在数组里。 16. KE.util.trim(str) 删除字符串两边的空格字符。 参数: str:String 返回值:String 17. KE.util.getJsKey(key) 把HTML style里的CSS名转换成JavaScript属性名。例如:KE.util.getJsKey("font-size")会返回"fontSize"。 参数: key:String 返回值:String 18. KE.util.escape(html) 转换HTML里的特殊字符。 参数: html:String,HTML文本 返回值:String 19. KE.util.getElementPos(el) 取得指定element的坐标。 参数: el:Object,element对象 返回值:Object 20. KE.util.getCoords(ev) 取得鼠标坐标。 参数: ev:Object,event对象 返回值:Object 21. KE.util.setOpacity(el, opacity) 设置element的透明度。 参数: el:Object,element对象 opacity:Int,透明度,可设置0到100的数字。 返回值:无 22. KE.util.getIframeDoc(iframe) 取得iframe document对象。 参数: iframe:Object,iframe对象 返回值:Object 23. KE.util.rgbToHex(str) 把RGB格式的颜色转换成16进制的颜色。 参数: str:String,RGB颜色标记 返回值:String 24. KE.util.createRange(doc) 创建指定document的range。 参数: doc:Object,document对象 返回值:Object,range对象 25. KE.util.getFullHtml(id, tagLineMode) 取得编辑器iframe的初始化HTML文本。 参数: id:String,编辑器的ID tagLineMode:Boolean,true时显示模块标签的轮廓。 返回值:String 26. KE.util.getData(id) 取得编辑器的HTML内容。 参数: id:String,编辑器的ID 返回值:String 27. KE.util.getSrcData(id) 取得编辑器的原生HTML内容,也就是innerHTML直接返回的HTML。 参数: id:String,编辑器的ID 返回值:String 28. KE.util.getPureData(id) 取得编辑器的纯文本内容,不包含HTML标签。3.4版本开始包含img和embed标签。 参数: id:String,编辑器的ID 返回值:String 29. KE.util.setData(id) 把编辑器的内容设置到原TEXTAREA控件里。 参数: id:String,编辑器的ID 返回值:无 30. KE.util.focus(id) 把焦点移到编辑器里。 参数: id:String,编辑器的ID 返回值:无 31. KE.util.selection(id) 把当前选中信息设置到KE.g[id].sel,KE.g[id].range,KE.g[id].keSel,KE.g[id].keRange里。 参数: id:String,编辑器的ID 返回值:无 32. KE.util.select(id) 重新选中range,仅在IE有效。 参数: id:String,编辑器的ID 返回值:无 33. KE.util.pToBr(id) 按下回车键时生成BR标签,仅在IE有效。 参数: id:String,编辑器的ID 返回值:无 注:3.4版本已废弃。 34. KE.util.execCommand(id, cmd, value) 执行浏览器自带的命令,详细请参考浏览器API里的document.execCommand。 参数: id:String,编辑器的ID cmd:String,浏览器execCommand里的cmd参数 value:String,浏览器execCommand里的value参数 返回值:无 35. KE.util.insertHtml(id, html) 把HTML内容插入到编辑区域里的光标处。 参数: id:String,编辑器的ID html:String,HTML内容 返回值:无 注:执行本函数之前必须先执行过 KE.util.selection(id),因为要先设置KE.g[id].sel和KE.g[id].range。 36. KE.create(id, mode) 创建编辑器。 参数: id:String,编辑器的ID mode:Int,可选参数,指定1时在body下面创建编辑器,0或未指定时在TEXTAREA前面创建编辑器。 返回值:无 37. KE.remove(id, mode) 移除编辑器。 参数: id:String,编辑器的ID mode:Int,可选参数,指定1时移除在body下面的编辑器,0或未指定时移除在TEXTAREA前面的编辑器。 返回值:无 38. KE.init(config) 设置编辑器的初始化参数。 参数: config:Object,编辑器属性的哈希数组,具体请参考编辑器属性 返回值:无 39. KE.show(config) 初始化并创建编辑器。执行本函数时先调用KE.init设置初始化参数,然后在DOM加载完成后执行KE.create。 参数: config:Object,编辑器属性的哈希数组,具体请参考编辑器属性 返回值:无 类 1. KE.selection(win, doc) KindEditor的selection类,取得或设置选中部分的range。 参数: win:Object,window对象 oc:Object,document对象 成员变量: sel:Object,浏览器原生selection对象 range:Object,当前selection的浏览器原生range对象 keRange:Object,当前selection的KindEditor range对象,请参考KE.range。 方法: addRange(keRange):设置当前selection。 focus():重新选中、仅在IE有效。 2. KE.range(doc) KindEditor的range类,为各浏览器提供统一的range接口。 参数: doc:Object,document对象 成员变量: startNode:Object,开始节点 startPos:Int,开始节点的位置 endNode:Object,结束节点 endPos:Int,结束节点的位置 方法: getParentElement():返回包含range的parent element。 getNodeList():返回range里的node list。 comparePoints(how, range):比较2个keRange的位置,how可以设置"START_TO_START", "START_TO_END", "END_TO_START","END_TO_END"。 setStart(node, pos):设置range的开始节点和位置。 setEnd(node, pos):设置range的结束节点和位置。 selectNode(node):把node设置到range,开始节点和结束节点都是node。 extractContents():提取range的内容。 cloneContents():复制range的内容。 getText():取得range的纯文本内容。 3. KE.cmd(id) KindEditor的命令类,类似execCommand。 参数: id:String,编辑器的ID 成员变量: doc:Object,编辑器的iframe document对象 keSel:Int,KindEditor selection对象 keRange:Object,当前selection的KindEditor range对象 方法: wrap(tagName, attributes):用指定标签包当前选中文本,目前只支持inline tag。tagName为标签名,attributes为该标签属性数组。 remove(tags):在当前选中文本中,清除指定的标签和属性。tags为你要删除的标签和属性。
http://kindeditor.net/demo.php
3.8.4、Kindeditor获取不了焦点的解决方法
使用layui弹出层时会发现kindeditor获取不了光标,这是因为多个层与iframe元素间叠放顺序的问题引起的,解决办法如下:
代码:
success: function(layero) { layer.setTop(layero); //顶置 editor = KindEditor.create('#details',{ uploadJson : 'js/kindeditor4111/jsp/upload_json.jsp', //上传程序 fileManagerJson : 'js/kindeditor4111/jsp/file_manager_json.jsp', //文件管理 allowFileManager : true , //是否允许上传 /* layui zIndex:19891014 KindEditor zIndex:811213 */ zIndex:99999999 }); //创建一个富文本编辑器
3.8.5、上传问题
富文本框,pageContext可能遇到上传目录不存在的问题
3.9、移动端UI框架
https://weui.io
http://www.jqweui.cn/
http://www.dcloud.io/mui.html
示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>我的生活</title> <meta name="viewport" content="initial-scale=1, maximum-scale=1" /> <link rel="shortcut icon" href="/favicon.ico" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> <link rel="stylesheet" href="js/suimobile/css/sm.min.css" /> <link rel="stylesheet" href="js/suimobile/css/sm-extend.min.css" /> </head> <body> <div class="page-group"> <!-- 你的html代码 --> <div class="page"> <header class="bar bar-nav"> <a class="button button-link button-nav pull-left" href="/demos/card"> <span class="icon icon-left"></span> 返回 </a> <h1 class="title">我的生活</h1> </header> <nav class="bar bar-tab"> <a class="tab-item external active" href="#"> <span class="icon icon-home"></span> <span class="tab-label">首页</span> </a> <a class="tab-item external" href="#"> <span class="icon icon-me"></span> <span class="tab-label">我</span> </a> <a class="tab-item external" href="#"> <span class="icon icon-star"></span> <span class="tab-label">收藏</span> </a> <a class="tab-item external" href="#"> <span class="icon icon-settings"></span> <span class="tab-label">设置</span> </a> </nav> <div class="content"> <!-- 这里是页面内容区 --> <div class="page-index" id="list"> </div> </div> </div> </div> <script type='text/javascript' src='js/suimobile/js/zepto.js' charset='utf-8'></script> <script type='text/javascript' src='js/suimobile/js/sm.min.js' charset='utf-8'></script> <script type='text/javascript' src='js/suimobile/js/sm-extend.min.js' charset='utf-8'></script> <script> $(function(){ $.init(); $.get("ProductController?action=getPager&limit=10&page=1",{},function(data){ $.each(data.data,function(index,obj){ c="<div class='card'>"; c+="<div style='background:url(\"images/"+obj.picture+"\") #fff center center' valign='bottom' class='card-header color-white no-border'>"+obj.name+"</div>"; c+="<div class='card-content'>"; c+="<div class='card-content-inner'>"; c+="<p class='color-gray'>发布日期 "+obj.addDate+"</p>"; c+="<p>此处是内容...</p>"; c+="</div>"; c+="</div>"; c+="<div class='card-footer'>"; c+="<a href='#' class='link'>赞</a>"; c+="<a href='#' class='link'>更多</a>"; c+="</div>"; c+="</div>"; $("#list").append(c); }); }); }); </script> </body> </html>
运行结果:
如果需要演示可使用Total Control软件
四、素材、示例与视频
4.1、素材
链接: https://pan.baidu.com/s/1iFyQJ8oNWnoVrQ64rq2c6w 密码: 5a19
图标:http://www.fontawesome.com.cn/
layui文档:http://www.layui.com/doc
layui文档:http://www.layui.com/demo/
上传jar包:https://files.cnblogs.com/files/best/%E4%B8%8A%E4%BC%A0jar.zip
KindEditor下载:链接: https://pan.baidu.com/s/1IGBFVzMPjQ5FYCEKoYOc9w 密码: tv9v
GoMallPro项目:链接: https://pan.baidu.com/s/1wN2DRJXGoTgPv4rSnCSWlw 密码: 13tw (SUI)