后台系统开发
后台管理系统工程
1、采用maven构建项目
- Maven定义了软件开发的整套流程体系,并进行了封装,开发人员只需要指定项目的构建流程,无需针对每个流程编写自己的构建脚本。除了项目构建,Maven最核心的功能是软件包的依赖管理,能够自动分析项目所需要的依赖软件包,并到Maven中心仓库去下载。并统一管理依赖的jar包。和工程之间的依赖关系。
- 使用Maven的本地仓库:
在当前系统用户的文件夹下。例如当前用户是Administrator那么本地仓库就是在C:UsersAdministrator.m2目录下。若要自定义仓库位置只需要修改Maven的配置文件即可。关于Maven的更多介绍可以参考这里。点我 - Maven插件使用eclipse mars自带maven插件。只需要统一开发环境。
2、创建工程
- 后台系统项目依赖关系
enjoyshop-parent 管理依赖jar包的版本,全局,公司级别,pom信息如下:
<groupId>com.enjoyshop.parent</groupId>
<artifactId>enjoyshop-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
enjoyshop-common –通用组件、工具类
<parent>
<groupId>com.enjoyshop.parent</groupId>
<artifactId>enjoyshop-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.enjoyshop.common</groupId>
<artifactId>enjoyshop-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
enjoyshop-manage –后台系统
<parent>
<groupId>com.enjoyshop.parent</groupId>
<artifactId>enjoyshop-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>enjoyshop-manage-pojo</module>
<module>enjoyshop-manage-mapper</module>
<module>enjoyshop-manage-service</module>
<module>enjoyshop-manage-web</module>
</modules>
<dependencies>
<dependency>
<groupId>com.enjoyshop.common</groupId>
<artifactId>enjoyshop-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!--省略了其他信息-->
</dependencies>
enjoyshop.manage.pojo
<parent>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>enjoyshop-manage-pojo</artifactId>
enjoyshop.manage.mapper
<parent>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>enjoyshop-manage-mapper</artifactId>
<dependencies>
<dependency>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage-pojo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!--省略了其他信息-->
</dependencies>
enjoyshop.manage.service
<parent>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>enjoyshop-manage-service</artifactId>
<dependencies>
<dependency>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage-mapper</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!--省略了其他信息-->
</dependencies>
enjoyshop.manage.web
<parent>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>enjoyshop-manage-web</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage-service</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!--省略了其他信息-->
</dependencies>
- 导入依赖和tomcat插件
导入依赖的原则:
1、在使用依赖的最底层导入。
2、运行时所需要的依赖在web工程中加入。
3、所有的工程都需要的依赖应该在聚合工程(enjoyshop-manage)中导入。
导入SSM依赖:
Spring和SpringMVC的依赖在enjoysho-manage-service中导入;
导入tomcat插件:
聚合工程的tomcat插件要在聚合工程中导入,enjoysho-manage中导入。
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8081</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
3、初步整合SSM
- 在enjoyshop.manage.web项目的相应目录下创建web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>enjoyshop-manage</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext*.xml</param-value>
</context-param>
<!--Spring的ApplicationContext 载入 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 编码过滤器,以UTF8编码 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 解决PUT请求无法提交表单数据的问题 -->
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 将POST请求转化为DELETE或者是PUT 要用_method指定真正的请求参数 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置SpringMVC框架入口 -->
<servlet>
<servlet-name>enjoyshop-manage</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/enjoyshop-manage-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>enjoyshop-manage</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
- 加入Sping的配置文件
applicationContext.xml(spring关于Bean的配置文件)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 使用spring自带的占位符替换功能 -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- 允许JVM参数覆盖 -->
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<!-- 忽略没有找到的资源文件 -->
<property name="ignoreResourceNotFound" value="true" />
<!-- 配置资源文件 -->
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!-- 扫描包 -->
<context:component-scan base-package="com.enjoyshop"/>
<!-- 定义数据源 -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
destroy-method="close">
<!-- 数据库驱动 -->
<property name="driverClass" value="${jdbc.driverClassName}" />
<!-- 相应驱动的jdbcUrl -->
<property name="jdbcUrl" value="${jdbc.url}" />
<!-- 数据库的用户名 -->
<property name="username" value="${jdbc.username}" />
<!-- 数据库的密码 -->
<property name="password" value="${jdbc.password}" />
<!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
<property name="idleMaxAge" value="30" />
<!-- 每个分区最大的连接数 -->
<!--
判断依据:请求并发数
-->
<property name="maxConnectionsPerPartition" value="100" />
<!-- 每个分区最小的连接数 -->
<property name="minConnectionsPerPartition" value="5" />
</bean>
</beans>
applicationContext-transaction.xml(Spring关于事务的配置文件)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 定义事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 定义事务策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--所有以query开头的方法都是只读的 -->
<tx:method name="query*" read-only="true" />
<!--其他方法使用默认事务策略 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<!--pointcut元素定义一个切入点,execution中的第一个星号 用以匹配方法的返回类型,这里星号表明匹配所有返回类型。 -->
<aop:pointcut id="myPointcut" expression="execution(* com.enjoyshop.manage.service.*.*(..))" />
<!--将定义好的事务处理策略应用到上述的切入点 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
</aop:config>
</beans>
- 加入SpringMVC的配置文件
enjoyshop-manage-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 定义注解驱动 -->
<mvc:annotation-driven/>
<!-- 定义Controller的扫描包 -->
<context:component-scan base-package="com.enjoyshop.manage.controller"/>
<!-- 定义试图解析器 -->
<!--
Example: prefix="/WEB-INF/jsp/", suffix=".jsp", viewname="test" -> "/WEB-INF/jsp/test.jsp"
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 加入Mybatis的配置文件
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<!-- 配置分页助手 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql" />
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
<property name="rowBoundsWithCount" value="true" />
</plugin>
<!-- 通用Mapper -->
<plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
<!--主键自增回写方法,默认值MYSQL,详细说明请看文档 -->
<property name="IDENTITY" value="MYSQL" />
<!--通用Mapper接口,多个通用接口用逗号隔开 -->
<property name="mappers" value="com.github.abel533.mapper.Mapper" />
</plugin>
</plugins>
</configuration>
- 关于SSM整合可以参考这里。点我
- 关于分页助手和通用Mapper可以参考这里。点我
4、导入数据库数据
关于数据库设计可以参考这里。点我
也可以直接到数据库SQL这个目录下找到SQL脚本。
5、实现页面的通用跳转
package com.enjoyshop.manage.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@RequestMapping("page")//这个类拦截类似/page/*这种请求
@Controller //标记为一个controller
public class PageController {
@RequestMapping(value = "{pageName}", method = RequestMethod.GET)
public String toPage(@PathVariable("pageName") String pageName) {
return pageName;
}
}
@RequestMapping(value = "{pageName}", method = RequestMethod.GET)
这行代码注解表示toPage这个方法接受一个占位符参数pageName(用大括号扩起来的就是占位符参数),并且指定了这个方法只响应GET请求。之后toPage通过
(@PathVariable("pageName") String pageName
这种形式来接受占位符参数,并把这个值赋给方法的输入参数pageName。
综合分析,如果请求的URL是/rest/page/index.jsp,那么首先SpringMVC会拦截/rest/*的请求,之后交给这个类来处理。此时传给toPage方法的占位符参数是“index”,那么该方法也就返回“index”这个字符串,经过springMVC解析后,返回WEB-INF/views/index.jsp这个视图。
6、登录逻辑的实现
- URL:rest/page/login.jsp
- 处理逻辑:
<script type="text/javascript">
$("#login").click(function(){
var username = $("[name=username]").val();
var password = $("[name=password]").val();
if(username!="admin" || password!="admin"){
$.messager.alert("错误","用户名密码不正确!");
return ;
}
window.location.href="/rest/page/index";
});
</script>
这里简化了用户登录的功能,直接检测用户名和密码是否为“admin”。在实际应用中应当连接数据库进行查询再返回结果。
7、首页菜单树的实现
- URL:rest/page/index.jsp
<body class="easyui-layout">
<div data-options="region:"west",title:"菜单",split:true" style="width:180px;">
<ul id="menu" class="easyui-tree" style="margin-top: 10px;margin-left: 5px;">
<li>
<span>商品管理</span>
<ul>
<li data-options="attributes:{"url":"/rest/page/item-add"}">新增商品</li>
<li data-options="attributes:{"url":"/rest/page/item-list"}">查询商品</li>
<li data-options="attributes:{"url":"/rest/page/item-param-list"}">规格参数</li>
</ul>
</li>
<li>
<span>网站内容管理</span>
<ul>
<li data-options="attributes:{"url":"/rest/page/content-category"}">内容分类管理</li>
<li data-options="attributes:{"url":"/rest/page/content"}">内容管理</li>
</ul>
</li>
</ul>
</div>
<div data-options="region:"center",title:""">
<div id="tabs" class="easyui-tabs">
<div title="首页" style="padding:20px;">
</div>
</div>
</div>
首页的布局通过easyui-layout来实现,而左侧菜单是通过定义一个easyui-tree来实现。
<ul id="menu" class="easyui-tree" style="margin-top: 10px;margin-left: 5px;">
- 菜单点击事件
<script type="text/javascript">
$(function(){
$("#menu").tree({
onClick: function(node){//node是点击的节点对象
if($("#menu").tree("isLeaf",node.target)){//判断当前节点是不是叶子节点,若是父节点则不作处理
var tabs = $("#tabs");
var tab = tabs.tabs("getTab",node.text);//通过叶子节点的名称查询tab
if(tab){//tab已存在,表示页面已打开,则选中打开的tab
tabs.tabs("select",node.text);
}else{
tabs.tabs("add",{
title:node.text,
href: node.attributes.url,
closable:true,
bodyCls:"content"
});
}
}
}
});
});
8、显示商品类目的功能实现
- 功能描述:在新增商品时点击选择类目,弹出窗口,在窗口中显示商品类目数据。
- 点击按钮描述
<tr>
<td>商品类目:</td>
<td>
<a href="javascript:void(0)" class="easyui-linkbutton selectItemCat">选择类目</a>
<input type="hidden" name="cid" style="width: 280px;"></input>
</td>
</tr>
class="easyui-linkbutton selectItemCat
指明了这个按钮的属性是一个easyui-linkbutton,关联的点击事件是selectItemCat。而这个selectItemCat的定义在common.js中。
- 点击弹出窗口的实现
// 初始化选择类目组件
initItemCat : function(data){
//i代表index索引,表示第几个元素
//e代表element元素
$(".selectItemCat").each(function(i,e){
var _ele = $(e);//把dom对象转为JQuery对象
if(data && data.cid){
_ele.after("<span style="margin-left:10px;">"+data.cid+"</span>");
}else{
_ele.after("<span style="margin-left:10px;"></span>");
}
_ele.unbind("click").click(function(){//解绑事件再绑定事件,可以防止重复绑定(绑定之前清除所有其他绑定)
$("<div>").css({padding:"5px"}).html("<ul>")//$("<div>")表示创建一个div,.css表示给予样式,.html写入ul元素
.window({
width:"500",
height:"450",
modal:true,
closed:true,
iconCls:"icon-save",
title:"选择类目",
onOpen : function(){//窗口打开时执行
- 加载并显示商品类目数据
onOpen : function(){//窗口打开时执行
var _win = this;//this指的是$("<div>")
$("ul",_win).tree({//$("ul",_win)表示在$("<div>")下去查找ul元素
url:"/rest/item/cat",
animate:true,
method:"GET",
onClick : function(node){
if($(this).tree("isLeaf",node.target)){
// 填写到cid中
_ele.parent().find("[name=cid]").val(node.id);//设置隐藏框的值为节点id
_ele.next().text(node.text).attr("cid",node.id);
$(_win).window("close");
if(data && data.fun){
data.fun.call(this,node);
}
}
}
});
},
onClose : function(){//窗口关闭事件
$(this).window("destroy");
}
- 在item-add.jsp上的初始化
以上的事件函数定义在一个名为ENJOYSHOP的对象中
var TT = ENJOYSHOP = {
·····
// 初始化选择类目组件
initItemCat : function(data){
······
}
}
然后再jsp页面上通过ENJOYSHOP.ini的方法实现初始化和加载。
$(function(){
itemAddEditor = ENJOYSHOP.createEditor("#itemAddForm [name=desc]");
ENJOYSHOP.init({fun:function(node){
ENJOYSHOP.changeItemParam(node, "itemAddForm");
}});
});
最后在TT.init中调用initItemCat方法,给class=”selectItemCat”绑定click事件,即可实现效果。
- 编写商品类目的POJO类
首先编写一个BasePojo类,存放一些基础通用数据。
package com.enjoyshop.manage.pojo;
import java.util.Date;
public abstract class BasePojo {
private Date created;
private Date updated;
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getUpdated() {
return updated;
}
public void setUpdated(Date updated) {
this.updated = updated;
}
}
编写ItemCat类,继承BasePojo
package com.enjoyshop.manage.pojo;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "tb_item_cat")
public class ItemCat extends BasePojo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long parentId;
private String name;
private Integer status;
private Integer sortOrder;
private Boolean isParent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getSortOrder() {
return sortOrder;
}
public void setSortOrder(Integer sortOrder) {
this.sortOrder = sortOrder;
}
public Boolean getIsParent() {
return isParent;
}
public void setIsParent(Boolean isParent) {
this.isParent = isParent;
}
// 扩展text字段,用于easyui的tree组件使用
public String getText() {
return this.getName();
}
//如果是父节点则状态为closed,如果是不是父节点则直接显示
public String getState() {
return this.getIsParent() ? "closed" : "open";
}
}
其中用到了JPA的注解。关于JPA注解请参考这里。点我
注意到该POJO类扩展了两个get方法:getText()和getState()方法,这是为了使得返回的数据满足EasyUI的tree的结构,否则EasyUI无法正确解析。
- 编写Mapper接口
只需继承通用Mapper接口即可。
package com.enjoyshop.manage.mapper;
import com.enjoyshop.manage.pojo.ItemCat;
import com.github.abel533.mapper.Mapper;
public interface ItemCatMapper extends Mapper<ItemCat>{
}
- 编写service层
首先创建一个BaseService,通过spring4的泛型注入特性,抽取出一些通用的方法。
package com.enjoyshop.manage.service;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.enjoyshop.manage.pojo.BasePojo;
import com.github.abel533.entity.Example;
import com.github.abel533.mapper.Mapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
public abstract class BaseService<T extends BasePojo> {
//public abstract Mapper<T> getMapper();
//spring4支持泛型注入
@Autowired
private Mapper<T> mapper;
/**
* 根据id查询数据
*
* @param id
* @return
*/
public T queryById(Long id) {
return this.mapper.selectByPrimaryKey(id);
}
/**
* 查询所有数据
*
* @return
*/
public List<T> queryAll() {
return this.mapper.select(null);
}
/**
* 根据条件查询一条数据
*
* @param record
* @return
*/
public T queryOne(T record) {
return this.mapper.selectOne(record);
}
/**
* 根据条件查询数据列表
*
* @param record
* @return
*/
public List<T> queryListByWhere(T record) {
return this.mapper.select(record);
}
/**
* 分页查询数据列表
*
* @param page
* @param rows
* @param record
* @return
*/
public PageInfo<T> queryPageListByWhere(Integer page, Integer rows, T record) {
// 设置分页参数
PageHelper.startPage(page, rows);
List<T> list = this.mapper.select(record);
return new PageInfo<T>(list);
}
/**
* 新增数据
*
* @param t
* @return
*/
public Integer save(T t) {
t.setCreated(new Date());
t.setUpdated(t.getCreated());
return this.mapper.insert(t);
}
/**
* 有选择的保存,选择不为null的字段作为插入字段
*
* @param t
* @return
*/
public Integer saveSelective(T t) {
t.setCreated(new Date());
t.setUpdated(t.getCreated());
return this.mapper.insertSelective(t);
}
/**
* 更新数据
*
* @param t
* @return
*/
public Integer update(T t) {
t.setUpdated(new Date());
return this.mapper.updateByPrimaryKey(t);
}
/**
* 有选择的更新,选择不为null的字段作为插入字段
*
* @param t
* @return
*/
public Integer updateSelective(T t) {
t.setUpdated(new Date());
return this.mapper.updateByPrimaryKeySelective(t);
}
/**
* 根据id删除数据
*
* @param id
* @return
*/
public Integer deleteById(Long id) {
return this.mapper.deleteByPrimaryKey(id);
}
/**
* 批量删除
*
* @param clazz
* @param property
* @param values
* @return
*/
public Integer deleteByIds(Class<T> clazz, String property, List<Object> values) {
Example example = new Example(clazz);
example.createCriteria().andIn(property, values);
return this.mapper.deleteByExample(example);
}
/**
* 根据条件删除数据
*
* @param record
* @return
*/
public Integer deleteByWhere(T record){
return this.mapper.delete(record);
}
}
编写ItemCatService,只需继承BaseService,注入Mapper接口即可,不需要编写具体的业务处理逻辑,这些都已经抽取到BaseService中。
package com.enjoyshop.manage.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.enjoyshop.manage.mapper.ItemCatMapper;
import com.enjoyshop.manage.pojo.ItemCat;
import com.github.abel533.mapper.Mapper;
@Service
public class ItemCatService extends BaseService<ItemCat> {
@Autowired
private ItemCatMapper itemCatMapper;
/**
* 根据父节点id查询商品类目列表
*
* @param parentId
* @return
*/
// public List<ItemCat> queryItemCatListByParentId(Long parentId) {
// ItemCat record = new ItemCat();
// record.setParentId(parentId);
// return this.itemCatMapper.select(record);
// }
public Mapper<ItemCat> getMapper() {
return this.itemCatMapper;
}
}
- 编写controller层
package com.enjoyshop.manage.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RequestParam;
import com.enjoyshop.manage.pojo.ItemCat;
import com.enjoyshop.manage.service.ItemCatService;
@RequestMapping("item/cat")
@Controller
public class ItemCatController {
@Autowired
private ItemCatService itemCatService;
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<ItemCat>> queryItemCatListByParentId(
@RequestParam(value = "id", defaultValue = "0") Long parentId) {
try {
// List<ItemCat> list = this.itemCatService.queryItemCatListByParentId(parentId);
ItemCat record = new ItemCat();
record.setParentId(parentId);
List<ItemCat> list = this.itemCatService.queryListByWhere(record);
if (null == list || list.isEmpty()) {
// 资源不存在
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.ok(list);
} catch (Exception e) {
e.printStackTrace();
}
// 500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
因为在rest中,如果要抛出异常或者给出出錯信息,则需要返回相关的HTTP 状态码。所以最简单的方法就是使用ResponseEntity作为返回值。
9、新增商品规格参数和查询商品参数列表的实现
- 前端实现
1、当新增商品规格参数时,需要先确认对应的规格参数已经是否存在。若已存在则弹出提示窗口
ENJOYSHOP.initItemCat({
fun:function(node){
$(".addGroupTr").hide().find(".param").remove();
// 判断选择的目录是否已经添加过规格
$.ajax({
type: "GET",
url: "/rest/item/param/" + node.id,
statusCode : {
200 : function(){
$.messager.alert("提示", "该类目已经添加,请选择其他类目。", undefined, function(){
$("#itemParamAddTable .selectItemCat").click();
});
},
404 : function(){
$(".addGroupTr").show();
},
500 : function(){
alert("error");
}
}
});
}
});
2、提交事件
$("#itemParamAddTable .submit").click(function(){
var params = [];
var groups = $("#itemParamAddTable [name=group]");
groups.each(function(i,e){
var p = $(e).parentsUntil("ul").parent().find("[name=param]");
var _ps = [];
p.each(function(_i,_e){
var _val = $(_e).siblings("input").val();
if($.trim(_val).length>0){
_ps.push(_val);
}
});
var _val = $(e).siblings("input").val();
if($.trim(_val).length>0 && _ps.length > 0){
params.push({
"group":_val,
"params":_ps
});
}
});
var url = "/rest/item/param/"+$("#itemParamAddTable [name=cid]").val();
$.ajax({
type: "POST",
url: url,
//JSON.stringify(params)将参数序列化为json格式
data: {"paramData":JSON.stringify(params)},
statusCode :{
201 : function(){
$.messager.alert("提示","新增商品规格成功!",undefined,function(){
$(".panel-tool-close").click();
$("#itemParamList").datagrid("reload");
});
},
400 : function(){
$.messager.alert("提示","提交的参数不合法!");
},
500 : function(){
$.messager.alert("提示","新增商品规格参数模板失败!");
}
}
});
});
这样提交的数据格式就是json格式。
3、查询规格参数列表的前端实现
<table class="easyui-datagrid" id="itemParamList" title="商品列表"
data-options="singleSelect:false,collapsible:true,pagination:true,url:"/rest/item/param",method:"get",pageSize:30,toolbar:itemParamListToolbar">
<thead>
<tr>
<th data-options="field:"ck",checkbox:true"></th>
<th data-options="field:"id",width:60">ID</th>
<th data-options="field:"itemCatId",width:80">商品类目ID</th>
<!-- <th data-options="field:"itemCatName",width:100">商品类目</th> -->
<th data-options="field:"paramData",width:300">规格</th>
<th data-options="field:"created",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">创建日期</th>
<th data-options="field:"updated",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">更新日期</th>
</tr>
</thead>
</table>
- 后台实现
service层关键代码
@Autowired
private ItemParamMapper itemParamMapper;
public PageInfo<ItemParam> queryPageList(Integer page, Integer rows) {
Example example=new Example(ItemParam.class);
example.setOrderByClause("updated DESC");
//设置分页参数
PageHelper.startPage(page,rows);
List<ItemParam> list=this.itemParamMapper.selectByExample(example);
return new PageInfo<ItemParam>(list);
}
controller层关键代码
@Autowired
private ItemParamService itemParamService;
//对应的url是/rest/item/param/itemCatId
//根据类目id查询模板数据
@RequestMapping(value="{itemCatId}",method=RequestMethod.GET)
public ResponseEntity<ItemParam> queryByItemCatId(@PathVariable("itemCatId") Long itemCatId){
try {
ItemParam record=new ItemParam();
record.setItemCatId(itemCatId);
ItemParam itemParam=this.itemParamService.queryOne(record);
if(null==itemParam){
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.ok(itemParam);
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
//对应的url是/rest/item/param/itemCatId,和上面的url相同,但是请求方式不同。一个是get,一个是post,这就是rest的功能
//保存商品规格参数模板
@RequestMapping(value="{itemCatId}",method=RequestMethod.POST)
public ResponseEntity<Void> saveItemParam(@RequestParam("paramData") String paramData,@PathVariable("itemCatId") Long itemCatId){
try {
ItemParam itemParam=new ItemParam();
itemParam.setItemCatId(itemCatId);
itemParam.setParamData(paramData);
this.itemParamService.save(itemParam);
return ResponseEntity.status(HttpStatus.CREATED).build();
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
//查询所有商品规格参数列表
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<EasyUIResult> queryItemParamList(@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "rows", defaultValue = "30") Integer rows) {
try {
PageInfo<ItemParam> pageInfo = this.itemParamService.queryPageList(page, rows);
EasyUIResult easyUIResult = new EasyUIResult(pageInfo.getTotal(), pageInfo.getList());
return ResponseEntity.ok(easyUIResult);
} catch (Exception e) {
e.printStackTrace();
}
// 出错 500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
10、新增商品的功能实现
- 商品价格的的处理
<tr>
<td>商品价格:</td>
<td><input class="easyui-numberbox" type="text" name="priceView" data-options="min:1,max:99999999,precision:2,required:true" />
<input type="hidden" name="price"/>
</td>
</tr>
设置一个隐藏框,商品显示时以元为单位,提交时以分为单位。
//处理商品的价格的单位,将元转化为分
$("#itemAddForm [name=price]").val(eval($("#itemAddForm [name=priceView]").val()) * 100);
//将编辑器中的内容同步到隐藏多行文本中
itemAddEditor.sync();
- 提交事件
//提交到后台的RESTful
$.ajax({
type: "POST",
url: "/rest/item",
data: $("#itemAddForm").serialize(),
statusCode : {
201 : function(){
$.messager.alert("提示","新增商品成功!");
},
400 : function(){
$.messager.alert("提示","提交的参数不合法!");
},
500 : function(){
$.messager.alert("提示","新增商品失败!");
},
404 : function(){
$.messager.alert("提示","页面不存在!");
}
}
});
- 显示商品规格参数
前端页面实现
changeItemParam : function(node,formId){
$.ajax({
type: "GET",
url: "/rest/item/param/" + node.id,
statusCode : {
200 : function(data){
$("#"+formId+" .params").show();
var paramData = JSON.parse(data.paramData);
var html = "<ul>";
for(var i in paramData){
var pd = paramData[i];
html+="<li><table>";
html+="<tr><td colspan="2" class="group">"+pd.group+"</td></tr>";
for(var j in pd.params){
var ps = pd.params[j];
html+="<tr><td class="param"><span>"+ps+"</span>: </td><td><input autocomplete="off" type="text"/></td></tr>";
}
html+="</li></table>";
}
html+= "</ul>";
$("#"+formId+" .params td").eq(1).html(html);
},
404 : function(){
$("#"+formId+" .params").hide();
$("#"+formId+" .params td").eq(1).empty();
},
500 : function(){
alert("error");
}
}
});
},
- service层关键代码
public void saveItem(Item item, String desc, String itemParams) {
// 设置初始数据
item.setStatus(1);
item.setId(null);// 强制设置id为null
super.save(item);
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(item.getId());
itemDesc.setItemDesc(desc);
// 保存描述数据
this.itemDescService.save(itemDesc);
// 保存规格参数数据
ItemParamItem itemParamItem = new ItemParamItem();
itemParamItem.setItemId(item.getId());
itemParamItem.setParamData(itemParams);
this.itemParamItemService.save(itemParamItem);
}
- controller层关键代码
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Void> saveItem(Item item, @RequestParam("desc") String desc,
@RequestParam("itemParams") String itemParams) {
try {
if (StringUtils.isEmpty(item.getTitle())) {
// 响应400
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
// 保存商品的基本数据
this.itemService.saveItem(item, desc, itemParams);
}
// 成功 201
return ResponseEntity.status(HttpStatus.CREATED).build();
} catch (Exception e) {
e.printStackTrace();
}
// 出错 500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
11、图片上传的实现
- 前端实现
前端图片的上传组件使用KindEditor的上传组件。通过如下方式指定了上传参数:
var TT = ENJOYSHOP = {
// 编辑器文件上传参数
kingEditorParams : {
filePostName : "uploadFile",
uploadJson : "/rest/pic/upload",
dir : "image"
},
具体的实现逻辑如下:
init : function(data){
this.initPicUpload(data);
this.initItemCat(data);
},
// 初始化图片上传组件
initPicUpload : function(data){
$(".picFileUpload").each(function(i,e){
var _ele = $(e);
_ele.siblings("div.pics").remove();
_ele.after("
<div class="pics">
<ul></ul>
</div>");
// 回显图片
if(data && data.pics){
var imgs = data.pics.split(",");
for(var i in imgs){
if($.trim(imgs[i]).length > 0){
_ele.siblings(".pics").find("ul").append("<li><a href=""+imgs[i]+"" target="_blank"><img src=""+imgs[i]+"" width="80" height="50" /></a></li>");
}
}
}
$(e).click(function(){
var form = $(this).parentsUntil("form").parent("form");//从按钮向上查找到最近的一个form表单
KindEditor.editor(TT.kingEditorParams).loadPlugin("multiimage",function(){
var editor = this;
editor.plugin.multiImageDialog({
//点击“全部插入”时执行
clickFn : function(urlList) {
var imgArray = [];
KindEditor.each(urlList, function(i, data) {
imgArray.push(data.url);
//小图片预览
form.find(".pics ul").append("<li><a href=""+data.url+"" target="_blank"><img src=""+data.url+"" width="80" height="50" /></a></li>");
});
//将URL通过逗号分隔写入隐藏域中
form.find("[name=image]").val(imgArray.join(","));
editor.hideDialog();
}
});
});
});
});
},
- 通过Nginx配置图片服务器
修改Nginx的配置文件nginx.conf,添加如下配置:
server {
listen 80;
server_name manage.enjoyshop.com;
#charset koi8-r;
#access_log logs/host.access.log main;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
server {
listen 80;
server_name image.enjoyshop.com;
#charset koi8-r;
#access_log logs/host.access.log main;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
root E:\picture-upload;
}
}
上面的配置中第一个server配置了后台系统的代理,使得URL:http://manage.enjoyshop.com指向http://127.0.0.1:8081。第二个server则配置了一个图片访问服务器,使得URL:http://image.enjoyshop.com指向了本地的静态资源E:picture-upload。与之相应的需要在本地的host文件中添加如下两个映射:
127.0.0.1 manage.enjoyshop.com
127.0.0.1 image.enjoyshop.com
关于Nginx的更多介绍请参考这里。点我
- 后台实现
1、首先在SpringMVC的配置文件中添加一个文件上传解析器。
<!-- 定义文件上传解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定默认编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 设定文件上传的最大值5MB,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
2、在类路径下新建一个外部属性文件env.properties,内容如下
REPOSITORY_PATH=E:\picture-upload
IMAGE_BASE_URL=http://image.enjoyshop.com
REPOSITORY_PATH是文件存储的物理路径。
IMAGE_BASE_UR是图片服务器的基础URL。
3、配置完成后在Spring的配置文件中加载该属性文件
<!-- 配置资源文件 -->
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
<value>classpath:env.properties</value>
</list>
</property>
4、创建对象来进行图片的存储和校验,只需存储其URL地址即可。通过宽和高两个属性来鉴别文件是否是图片。
package com.enjoyshop.manage.bean;
public class PicUploadResult {
//上传是否成功的判断标识,0-成功,1-失败
private Integer error;
private String url;
private String width;
private String height;
public Integer getError() {
return error;
}
public void setError(Integer error) {
this.error = error;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getWidth() {
return width;
}
public void setWidth(String width) {
this.width = width;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
}
5、定义service层
package com.enjoyshop.manage.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class PropertieService {
@Value("${REPOSITORY_PATH}")
public String REPOSITORY_PATH;
@Value("${IMAGE_BASE_URL}")
public String IMAGE_BASE_URL;
}
service层中通过 @Value(“${xxxx}”)的注解获取到了Spring加载的外部属性文件中的值。
需要注意的是不能将这两个属性放入controller中,因为@Value作用是在Spring容器初始化(所有的bean)之后,在当前的所在容器中获取值,然后注入。假如把这两个属性放入controller中,那么相应的当前容器就是SpringMVC容器,而SpringMVC容器中并没有注入相应的值。因为我们是在Spring的配置文件中加载的外部属性文件,相应的值实际上是在Spring容器中。
关于Spring的父子容器:
Spring容器是父容器,SpringMVC容器是子容器。
父子容器的关系:
(1)、 子容器能够访问父容器的资源(bean)
示例:Controller(SpringMVC容器)可以注入Service(Spring容器)
(2)、 父容器不能访问子容器的资源(bean)
示例:无法在service层中注入controller
6、定义controller层
/**
* 图片上传
*/
@Controller
@RequestMapping("/pic")
public class PicUploadController {
private static final Logger LOGGER = LoggerFactory.getLogger(PicUploadController.class);
private static final ObjectMapper mapper = new ObjectMapper();
@Autowired
private PropertieService propertieService;
// 允许上传的格式
private static final String[] IMAGE_TYPE = new String[] { ".bmp", ".jpg", ".jpeg", ".gif", ".png" };
//produces: 指定响应的类型
@RequestMapping(value = "/upload", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String upload(@RequestParam("uploadFile") MultipartFile uploadFile, HttpServletResponse response)
throws Exception {
// 校验图片格式
boolean isLegal = false;
for (String type : IMAGE_TYPE) {
if (StringUtils.endsWithIgnoreCase(uploadFile.getOriginalFilename(), type)) {
isLegal = true;
break;
}
}
// 封装Result对象,并且将文件的byte数组放置到result对象中
PicUploadResult fileUploadResult = new PicUploadResult();
// 状态
fileUploadResult.setError(isLegal ? 0 : 1);
// 文件新路径
String filePath = getFilePath(uploadFile.getOriginalFilename());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Pic file upload .[{}] to [{}] .", uploadFile.getOriginalFilename(), filePath);
}
// 生成图片的绝对引用地址
String picUrl = StringUtils.replace(StringUtils.substringAfter(filePath, propertieService.REPOSITORY_PATH),
"\", "/");
fileUploadResult.setUrl(propertieService.IMAGE_BASE_URL + picUrl);
File newFile = new File(filePath);
// 写文件到磁盘
uploadFile.transferTo(newFile);
// 校验图片是否合法
isLegal = false;
try {
BufferedImage image = ImageIO.read(newFile);
if (image != null) {
fileUploadResult.setWidth(image.getWidth() + "");
fileUploadResult.setHeight(image.getHeight() + "");
isLegal = true;
}
} catch (IOException e) {
}
// 状态
fileUploadResult.setError(isLegal ? 0 : 1);
if (!isLegal) {
// 不合法,将磁盘上的文件删除
newFile.delete();
}
//将java对象转化成(序列化)json字符串
return mapper.writeValueAsString(fileUploadResult);
}
private String getFilePath(String sourceFileName) {
String baseFolder = propertieService.REPOSITORY_PATH + File.separator + "images";
Date nowDate = new Date();
// yyyy/MM/dd
String fileFolder = baseFolder + File.separator + new DateTime(nowDate).toString("yyyy")
+ File.separator + new DateTime(nowDate).toString("MM") + File.separator
+ new DateTime(nowDate).toString("dd");
File file = new File(fileFolder);
if (!file.isDirectory()) {
// 如果目录不存在,则创建目录
file.mkdirs();
}
// 生成新的文件名
String fileName = new DateTime(nowDate).toString("yyyyMMddhhmmssSSSS")
+ RandomUtils.nextInt(100, 9999) + "." + StringUtils.substringAfterLast(sourceFileName, ".");
return fileFolder + File.separator + fileName;
}
}
因为需要把上传文件返回的数据设为文本类型的json数据,所以这里使用String作为方法的返回值类型,并且用@ResponseBody来注解该方法使其返回JSON数据。
12、实现商品列表的显示
- 前台页面
<table class="easyui-datagrid" id="itemList" title="商品列表"
data-options="singleSelect:false,collapsible:true,pagination:true,url:"/rest/item",method:"get",pageSize:30,toolbar:toolbar">
<thead>
<tr>
<th data-options="field:"ck",checkbox:true"></th>
<th data-options="field:"id",width:60">商品ID</th>
<th data-options="field:"title",width:200">商品标题</th>
<th data-options="field:"cid",width:100">叶子类目</th>
<th data-options="field:"sellPoint",width:100">卖点</th>
<th data-options="field:"price",width:70,align:"right",formatter:ENJOYSHOP.formatPrice">价格</th>
<th data-options="field:"num",width:70,align:"right"">库存数量</th>
<th data-options="field:"barcode",width:100">条形码</th>
<th data-options="field:"status",width:60,align:"center",formatter:ENJOYSHOP.formatItemStatus">状态</th>
<th data-options="field:"created",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">创建日期</th>
<th data-options="field:"updated",width:130,align:"center",formatter:ENJOYSHOP.formatDateTime">更新日期</th>
</tr>
</thead>
</table>
可以看到请求的URL为/rest/item,请求方式method是get。默认情况下,会直接显示返回的数据,但是有些属性下不能直接显示,如:价格、日期、性别,需要指定formatter函数。在common.js中有如下定义:
Date.prototype.format = function(format){
var o = {
"M+" : this.getMonth()+1, //month
"d+" : this.getDate(), //day
"h+" : this.getHours(), //hour
"m+" : this.getMinutes(), //minute
"s+" : this.getSeconds(), //second
"q+" : Math.floor((this.getMonth()+3)/3), //quarter
"S" : this.getMilliseconds() //millisecond
};
if(/(y+)/.test(format)){
format = format.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
}
for(var k in o) {
if(new RegExp("("+ k +")").test(format)){
format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length));
}
}
return format;
};
// 格式化时间
formatDateTime : function(val,row){
var now = new Date(val);
return now.format("yyyy-MM-dd hh:mm:ss");
},
// 格式化连接
formatUrl : function(val,row){
if(val){
return "<a href=""+val+"" target="_blank">查看</a>";
}
return "";
},
// 格式化价格
formatPrice : function(val,row){
return (val/1000).toFixed(2);
},
/
- 上一篇: php给图片添加文字水印
- 下一篇: PostgreSQL 联结方式--排序-合并联结