CRM系统

注册

bean包下 建用户类 User.java 提供set/get/tostring方法

private Long user_id;//用户id
private String user_code;  //用户账号
private String user_name; //用户昵称
private String user_password;//用户密码
private char user_state;  //用户状态 : 1 : 可用 ,0 : 暂停使用

User.hbm.xml 关联会生成 sys_user表

<hibernate-mapping>
	<class name="com.itheima.bean.User" table="sys_user">
		<id name="user_id">
			<generator class="native"></generator>
		</id>
		<property name="user_code"/>
		<property name="user_name"/>
		<property name="user_password"/>
		<property name="user_state"/>
	</class>
</hibernate-mapping>

register.jsp 的前端请求

<form action="${pageContext.request.contextPath }/user_register" method="post">
用户名: <input type="text" name="user_code"/><br>
用户密码: <input type="password" name="user_password"/><br>
用户昵称: <input type="text" name="user_name"/><br>
input type="submit"  value="注册"/></form>

UserAction.java 新建在 web.action包下

public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user;//声明user对象
@Override
public User getModel() {//重写getModel方法new出user对象并返回
	if (user==null) {
	 user = new User();		} 
	return user;	}

private UserService userService;//声明userService对象
public void setUserService(UserService userService) {//提供set方法
	this.userService = userService;}

public String register() {//注册
user.setUser_state('1');//设置状态为1
userService.register(user);//userService 注册业务
return NONE;}//返回 为无

接口UserService

public interface UserService {	void register(User user);}

UserService接口 实现类UserServiceImpl 并开启事务@Transactional

@Transactional
public class UserServiceImpl implements UserService {
	private UserDao userDao;//声明userDao接口
	public void setUserDao(UserDao userDao) {//提供set方法
		this.userDao = userDao;	}
		
	@Override
	public void register(User user) {//重写接口UserService 方法 
	String pwd = Md5Util.encodePwd(user.getUser_password());//把密码 加密
		user.setUser_password(pwd);//设置封闭进去
		userDao.save(user);	}//调用userDao接口的.save方法

接口UserDao

public interface UserDao {void save(User user);}

UserDao接口 实现类UserDaoImpl extends HibernateDaoSupport

public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
	@Override
public void save(User user) {//重写接口方法
	getHibernateTemplate().save(user);}}//继承getHibernateTemplate父类的save方法把user扔进去就好

applicationContext.xml

<!-- 以下属于UserAction模块配置 多例 -->
<bean id="userAction" class="com.it.web.action.UserAction" scope="prototype">
	<property name="userService" ref="userService"></property>
</bean>
<bean id="userService" class="com.it.service.impl.UserServiceImpl">
	<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.it.dao.impl.UserDaoImpl">
	<property name="sessionFactory" ref="sessionFactory"></property>
</bean>

struts.xml配置:前端的请求以user_*开头的请求会到这里 然后指定一组请求集合package名user里的请求的Aiton类全限定名地址 result设置处理后的请求转发地址

<struts>
  <package name="user" namespace="/" extends="struts-default">
  	<action name="user_*" class="userAction" method="{1}" ></action>
  </package>
</struts>

登陆

前端 login.jsp

<FORM id=form1 name=form1 action="${pageContext.request.contextPath}/user_login" method="post">

UserAction.java里新建 login登陆方法

public String login() {//登陆
User loginUser=userService.login(user);//调登陆业务返回loginUser
if (loginUser!=null) {//登陆成功 存到session
ServletActionContext.getRequest().getSession().setAttribute("user", loginUser);
	return Constant.LOGIN_SUCCESS;//返回到常量接口里的常量 常量再赋值给login_success
}
//ActionContext.getContext().getValueStack().set("msg", "帐号或密码错误");//存到值栈
addFieldError("msg", "帐号或密码错误");//采用struts里的方法回显错误 取值${fieldErrors.msg[0] }
return Constant.LOGIN_ERROR;}//返回 失败

interface UserService接:User login(User user); UserServiceImpl实现类:

public User login(User user) {
		String pwd = Md5Util.encodePwd(user.getUser_password());//也把密码加密
		user.setUser_password(pwd);//设置封闭进去
		return userDao.findUser(user);

interface UserDao接口:User findUser(User user); UserDaoImpl实现类

public User findUser(User user) {
String hql="from User where user_code=? and user_password=? and user_state=1";
List<User> list = (List<User>) getHibernateTemplate().find(hql, user.getUser_code(),user.getUser_password());//spring里的模板方法得到的是list
	if (list.size()>0) {
		return list.get(0);//返回list里面的user 对象
	}
	return null;}

index.jsp框架结构

<FRAMESET frameSpacing=0 rows=80,* frameBorder=0> <!-- 框架划分上下 上面划分80像素下面占剩下所有 -->
	<FRAME name=top src="top.jsp" frameBorder=0 noResize scrolling=no><!-- 上面放的内容 -->
	<FRAMESET frameSpacing=0 frameBorder=0 cols=220,*><!-- 下面再放个框架 再划分左右 左占220像素右占剩下所有 -->
		<FRAME name=menu src="${pageContext.request.contextPath}/menu.jsp" frameBorder=0 noResize><!-- 左边 -->
		<FRAME name=main src="${pageContext.request.contextPath}/welcome.htm" frameBorder=0><!-- 右边 -->
</FRAMESET>

struts.xml

<package name="user" namespace="/" extends="struts-default">
	<action name="user_*" class="userAction" method="{1}">
		<result name="login_success" type="redirect">/index.jsp</result>
		<result name="login_error">/login.jsp</result>
	</action>
</package>

成功 就返回index.jsp 里框架里的top.jsp里展示昵称: 当前用户:${user.user_name } 方式重定向type=“redirect”

在top.jsp上面显示登陆的昵称

<tr><td height=35 align="right">当前用户:${user.user_name }&nbsp;<a href="#" >修改密码...

失败就返回login.jsp 在登陆名后面显示错误信息 struts里的方法回显 ${fieldErrors.msg[0] }

客户管理–新增客户/customer

index.jsp是个框架用<frameset>代替 <body> 左边是menu.jsp 增加客户

<A class=style2 href="${pageContext.request.contextPath}/jsp/customer/add.jsp" target=main>- 新增客户</A>

点击增加客户链接后 在框架的右边就显示add.jsp 里是个表单

<FORM id=form1 name=form1 action="${pageContext.request.contextPath }/customer_save" method=post>

bean包里 Customer 新增 客户类 都提供get/set/tostring方法

	private Long cust_id;//客户id
	private String cust_name;//客户名称
	/*private String cust_source;
	private String cust_industry;
	private String cust_level;*/
	
	//表示这个客户属于哪一种来源 用 字典对象
	private BaseDict cust_source;//信息来源来源
	private BaseDict cust_industry;//所属行业
	private BaseDict cust_level;//客户级别
	
	private String cust_phone;//移动电话
	private String cust_address;//联系地址
	
	private String cust_user_id;//负责人
	private String cust_create_id;//创建人

Customer.hbm.xml 生成cst_customer客户表 客户和字典的关系是: 多对一

<hibernate-mapping>
	<class name="com.it.bean.Customer" table="cst_customer">
		<id name="cust_id">
			<generator class="native"></generator>
		</id>
		<property name="cust_name"/>
		<property name="cust_phone"/>
		<property name="cust_address"/>
		<property name="cust_user_id"/>
		<property name="cust_create_id"/>
		<!-- 这里要体现客户和字典的关系了,客户和字典的关系是: 多对一  -->
		<many-to-one name="cust_source" class="com.it.bean.BaseDict"/>
		<many-to-one name="cust_industry" class="com.it.bean.BaseDict"/>
		<many-to-one name="cust_level" class="com.it.bean.BaseDict"/>
	</class>
</hibernate-mapping>

跑起来就会自动建表了

CustomerAction 客户的action

public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
	private Customer customer;//声明customer/客户 对象 
	@Override
	public Customer getModel() {//返回客户对象
		if (customer==null) {
			customer = new Customer();
		}
		return customer;	}
	
	private CustomerService customerService;//声明接口
	public void setCustomerService(CustomerService customerService) {//提供set方法
		this.customerService = customerService;	}
	
	public String save() {//新增保存客户业务
		customerService.save(customer);
		return NONE;		}}

interface CustomerServic 接口 void save(Customer customer); 实现类CustomerServiceImpl

public class CustomerServiceImpl implements CustomerService {
	private CustomerDao customerDao;//声明dao接口
	@Override
	public void save(Customer customer) {
		customerDao.save(customer);//方法
	}

interface CustomerDao接口 void save(Customer customer);实现类CustomerDaoImpl

public class CustomerDaoImpl extends HibernateDaoSupport implements CustomerDao {
	@Override
	public void save(Customer customer) {
		getHibernateTemplate().save(customer);	}//使用父类 模板方法保存客户

acclicationContext.xml

 <!-- 以下属于客户模块 -->
<bean id="customerAction" class="com.it.web.action.CustomerAction" scope="prototype">
	<property name="customerService" ref="customerService"></property>
</bean>
<bean id="customerService" class="com.it.service.impl.CustomerServiceImpl">
	<property name="customerDao" ref="customerDao"></property>
</bean>
<bean id="customerDao" class="com.it.dao.impl.CustomerDaoImpl">
	<property name="sessionFactory" ref="sessionFactory"></property>
</bean>

struts

  <!-- 以下属于客户模块 -->
<package name="customer" namespace="/" extends="struts-default">
	<action name="customer_*" class="customerAction" method="{1}">
	</action>
</package>

客户来源 客户行业 客户级别的三个下拉业务 字典bean

BaseDict 数据字典类 规范来源 bean包下 用于规范数据来源

	private String dict_id;  //下拉 主键
	private String dict_type_code; //下拉 类型的代号 001所属行业/002来源
	private String dict_type_name;//类型 名称
	private String dict_item_name;// 来源行业级别 三个项目的名称
	private String dict_item_code;//来源行业级别 三个项目的 项目
	private int dict_sort;//排序字段
	private char dict_enable;//是否停用 1使用
	private String dict_memo;//备注

前端请求 是js请求

<script type="text/javascript">
function loadDict(type_code , tagId){
		//发起请求,获取字典数据 按类型查询字典数据
		var url = "${pageContext.request.contextPath }/baseDict_findByType";
		$.post(url , {"dict_type_code" :type_code } , function(result){
			//result -- list<BaseDict>  -- json
			$(result).each(function(i , n){ //i : 遍历的下标,  n : 遍历出来的对象   字典对象
				//var a = 3;
				//找到标签,然后追加内容 val(aa) , text(aa) , html(xxx) , append(xxx)
				$(tagId).append("<option value='"+n.dict_id+"'>"+n.dict_item_name+"</option>")
			});
			
		} , "json");
	}
	$(function(){
		loadDict("001" , "#cust_industry"); //001  -- 客户行业
		loadDict("002" , "#cust_source"); //002  -- 客户来源
		loadDict("006" , "#cust_level");//006  -- 客户级别
	})
</script>

BaseDictAction 的action 里面的 查询下拉业务

public class BaseDictAction extends ActionSupport  {
	private String  dict_type_code;//声明 下拉类型的代号 001所属行业
	public void setDict_type_code(String dict_type_code) {//提供set方法
		this.dict_type_code = dict_type_code;	}
	
	private BaseDictService baseDictService;//声明接口
	public void setBaseDictService(BaseDictService baseDictService) {
		this.baseDictService = baseDictService;}//返回接口
		
	public String findByType(){//查询下拉的 业务
		try {
			List<BaseDict> list = baseDictService.findByType(dict_type_code);//1. 查询数据
			String json = new Gson().toJson(list);//2. list --> json 
			HttpServletResponse response = ServletActionContext.getResponse();//3. 写给页面。
			response.setContentType("text/html;charset=utf-8");
			response.getWriter().write(json);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return NONE;}

使用Struts方式返回json数据 /下拉业务的 信息来源 行业 级别

BaseDictAction 的action 里面的 查询下拉业务

public class BaseDictAction extends ActionSupport  {
	private String  dict_type_code;//声明 下拉类型的代号 001所属行业
	public void setDict_type_code(String dict_type_code) {//提供set方法
		this.dict_type_code = dict_type_code;	}
	
private BaseDictService baseDictService;//声明接口
public void setBaseDictService(BaseDictService baseDictService) {
		this.baseDictService = baseDictService;}//返回接口
	
private List<BaseDict>list;//字典集合 变量
public List<BaseDict> getList() {//提供get方法
return list;	}
//改成struts方式返回json数据 直接在struts里配置跳转成json类型
public String findByType(){
list = baseDictService.findByType(dict_type_code);//1. 查询数据业务
return Constant.JSON_SUCCESS;}//直接返回 常量 String JSON_SUCCESS = "json_success";

struts.xml root为固定写法把list跳转成json类型 extends=“json-default

<!-- 以下属于字典模块 -->
	<package name="baseDict" namespace="/" extends="json-default">
		<action name="baseDict_*" class="baseDictAction" method="{1}">
			<result name="json_success" type="json">
				<param name="root">list</param><!-- root为固定写法把list跳转成json类型  -->
			</result> 
		</action>
	</package>

这时候点提交会报错 直接返回会生成一条空的客户http://localhost/crm28_511/customer_save

要在add.jsp 里把三个下拉的 改成字典里的dict_id <select name=“cust_industry.dict_id 就可以了

数据校验

可以在前端做js校验 也可以在后台做校验 CustomerAction.java里的添加客户 save方法前对所有数据作校验

public String save(){
		//在提交前对所有数据都进行校验 是springframework.util.StringUtils包提供的工具类;
		if (StringUtils.isEmpty(customer.getCust_name())) {
			addActionError("客户名称不能为空");//错误提示
			return Constant.INPUT_ERROR;
		}
		if (StringUtils.isEmpty(customer.getCust_address())) {
			addActionError("客户地址不能为空");//错误提示的值栈
			return Constant.INPUT_ERROR;
		}
		..电话
		}
		if (StringUtils.isEmpty(customer.getCust_industry().getDict_id())) {
			addActionError("客户行业没选");//错误提示
			return Constant.INPUT_ERROR;
		}..来源 级别

struts.xml 请求转发回来显示

	<!-- 以下属于客户模块 -->
	<package name="customer" namespace="/" extends="struts-default">
		<action name="customer_*" class="customerAction" method="{1}">
			<result name="input_error">/jsp/customer/add.jsp</result>
		</action>
	</package>

add.jsp

<%@ taglib uri="/struts-tags” prefix=“s” %>

在结果 后面加一列 里面显示值栈 <td>${actionErrors[0] }<td>

回显 漏掉的数据 不会重复

文本框回显 : 把 <input 换成<s:textfield 并属性值都要打双引号

<s:textfield class="textbox" id="sChannel2" style="WIDTH: 180px" maxLength="50" name="cust_address" />

布局乱了:原理是struts标签的默认主题是xhtml 生成的html标签是使用表格来布局 改一个struts.xml常量主题设成simple

<struts> <constant name="struts.ui.theme" value="simple"></constant>

下拉框回显: 1要知道提交上去是什么数据 2 提交上去的是value=2 然后用2去selectj里挨个问 并设置为selected就好

<script type="text/javascript">
$(function(){//加载完就 执行三个方法 下拉
		loadDict("001" , "#cust_industry" , "${cust_industry.dict_id}"); //001  -- 客户行业
		loadDict("002" , "#cust_source" , "${cust_source.dict_id}"); //002  -- 客户来源
		loadDict("006" , "#cust_level" , "${cust_level.dict_id}");//006  -- 客户级别
	})
	function loadDict(type_code , tagId , oldVal){//传旧的值进来回显
		//发起请求,获取字典数据 按类型查询字典数据
		var url = "${pageContext.request.contextPath }/baseDict_findByType";
		$.post(url , {"dict_type_code" :type_code } , function(result){
			$(result).each(function(i , n){ //i : 遍历的下标,  n : 遍历出来的对象   字典对象
				$(tagId).append("<option value='"+n.dict_id+"'>"+n.dict_item_name+"</option>")
			});
			//选中该选中的option  在指定的id标签身上找option标签 按照value值来找,如果找到了就修改这个option标签的属性,修改selected属性,值为selected
			$(tagId).find("option[value='"+oldVal+"']").attr("selected","selected");
		} , "json");	}
</script>

图片上传

add.jsp 在联系电话后加一个 文件上传标签file

<tr><td>客户资质 </td>
<td>
<input type="file" name="upload"/>
</td></tr>

并在表单下面写

<!-- enctype="application/x-www-form-urlencoded" : 表示提交上去的是一份经过url编码的form表单数据,主要针对文本数据	enctype="multipart/form-data" : 提交上来的是表单数据,包含多份, 有表单数据也有文件数据 -->
	<FORM id=form1 name=form1 action="${pageContext.request.contextPath }/customer_save" method=post
		enctype="multipart/form-data">

CustomerAction 的save()方法里处理 先声明对象

//struts获取文件数据 需要声明文件对象和以下三个并提供set方法
	private File upload;//File对象 java.io.File 值是file标签的name属性值
	private String uploadContextType;//文件类型 name属性值+ContextType固定写法
	private String uploadFileName;//文件名称 name属性值+FileName固定写法
	public void setUpload(File upload) {
		this.upload = upload;	}
	public void setUploadContextType(String uploadContextType) {
		this.uploadContextType = uploadContextType;	}
	public void setUploadFileName(String uploadFileName) {
		this.uploadFileName = uploadFileName;	}
		
public String save() throws IOException{
		//存储文件数据把tem转成jpg|png 
	File file=new File("d://hm28", MyFileUtil.getFileName(uploadFileName));//文件对象 文件名拼接为要存的路径+工具类静态方法生成的随机名
	FileUtils.copyFile(upload, file);//org.apache.commons.io 原文件选属性值 和目标文件

上传限制 在struts.xml里 设置为200m

<constant name="struts.multipart.maxSize" value="209715200"></constant>

客户列表

<tr> 
	<td class=menuSmall>
		<a class=style2 href="${pageContext.request.contextPath}/customer_findByPage.action" target=main>- 客户列表</a>
	</td>
</tr>

CustomerAction里的分页显示客户列表 public String findByPage() {

public String findByPage() {
	//离线对象如果只是这么创建出来,背后的sql语句 select * from customer;
	//只会使用离线对象的两个方法  setXXX(针对聚合查询 总记录数、最大值、最小值、)  addXXX(针对where条件的设置)
	DetachedCriteria criteria  =DetachedCriteria.forClass(Customer.class);//离线对象 [dɪˈtætʃt] [kraɪˈtɪrɪə]
	//2. 查询数据业务,有返回值
	PageBean<Customer> pageBean = customerService.findByPage(criteria , currentPage , pageSize);
	ActionContext.getContext().getValueStack().push(pageBean);//3. 把pageBean存储到作用域 值栈。 push | set | 属性
	return Constant.PAGE_SUCCESS;}//返回

PageBean.java bean包下 提供set/get/tostring方法

public class PageBean<T> {
private int currentPage;//当前页码
private int totalPage;//总页数

private int pageSize;//每页条数
private int totalSize;//总条数
private List<T>list;//当前页集合

到 CustomerServiceImpl 分页显示客户列表 有两个地方会走这个方法:

@Transactional//注解业务
public class CustomerServiceImpl implements CustomerService {
..
private int currentPage=1;//先设置 默认第一页/当前页 提供set方法以便页面修改
private int pageSize=5;//设置每页条数 提供set方法以便页面修改获取具体条数
public void setCurrentPage(int currentPage) {
	this.currentPage = currentPage;}
public void setPageSize(int pageSize) {
	this.pageSize = pageSize;}
	
public String findByPage() {//分布查询客户列表 两个地方走这个方法左侧客户列表不带条件 筛选带条件
//离线对象如果只是这么创建出来,背后的sql语句 select * from customer;是QBC查询重要组件 能够在dao层之上两层进行查询条件封装 也可以不封装
//只会使用离线对象的两个方法  setXXX(针对聚合查询 总记录数、最大值、最小值、addXXX(针对where条件的设置)
DetachedCriteria criteria=DetachedCriteria.forClass(Customer.class);//离线对象
PageBean<Customer> pageBean = customerService.findByPage(criteria , currentPage , pageSize);//查询数据业务 返回pageBean 集合对象
ActionContext.getContext().getValueStack().push(pageBean);//3. 把pageBean存储到作用域 值栈。 push | set | 属性
return Constant.PAGE_SUCCESS;}//返回

service接口 CustomerService

public interface CustomerService {
	PageBean<Customer> findByPage(DetachedCriteria criteria, int currentPage, int pageSize);

service实现类 CustomerServiceImpl

public PageBean<Customer> findByPage(DetachedCriteria criteria, int currentPage, int pageSize) {
int totalSize=customerDao.findCound(criteria);//查出总条数 传离线对象 区分筛选某一种类型数据
List<Customer>list=customerDao.findByPage(criteria,currentPage,pageSize);//查出PageBean的list

PageBean<Customer> pageBean = new PageBean<>();//new pageBean对象
pageBean.setCurrentPage(currentPage);//手动封装 当前页

int totalPage=totalSize%pageSize==0?totalSize/pageSize:(totalSize/pageSize)+1;//三元计算出总页数 整除就总页除每页条数,不整除加1
//int totalPage=(int)Math.ceil(totalSize*1.0/pageSize);//也可以用工具类向上取整只要有小数点就向上取成整数
pageBean.setTotalPage(totalPage);//手动封装 总页数

pageBean.setPageSize(pageSize);//手动封装 每页条数
pageBean.setTotalSize(totalSize);//手动封装 总条数

pageBean.setList(list);//手动封装pageBean
return pageBean;}//返回的是pageBean

dao接口 CustomerDao

public interface CustomerDao {
	int findCound(DetachedCriteria criteria);
	List<Customer> findByPage(DetachedCriteria criteria, int currentPage, int pageSize);

dao实现 CustomerDaoImpl

@Override//查询客户总条数 用hibernate模板查
	public int findCound(DetachedCriteria criteria) {
		criteria.setProjection(Projections.rowCount());//离线对象经过这个语句就变成查select count(*) from customer查行个数 默认是查全部select*from customer
		List<Long> list = (List<Long>) getHibernateTemplate().findByCriteria(criteria);
		if (list.size()>0) {
			return list.get(0).intValue();//如果集合中有条数  就转成int
		}
		return 0;	}

	@Override//查分页
	public List<Customer> findByPage(DetachedCriteria criteria, int currentPage, int pageSize) {
		criteria.setProjection(null);//离线对象 重置回来select*from customer
		return (List<Customer>) getHibernateTemplate().findByCriteria(criteria, (currentPage-1)*pageSize, pageSize);	}//查分页 离线对象 当前页减1再乘每页条数 每页条数

struts.xml

<!-- 以下属于客户模块 -->
	<package name="customer" namespace="/" extends="struts-default">
		<action name="customer_*" class="customerAction" method="{1}">
			<result name="page_success">/jsp/customer/list.jsp</result>

/jsp/customer/list.jsp

<%@ taglib uri="/struts-tags” prefix=“s” %>

<!-- 遍历存在值栈中的list 的每一条客户信息 并展示各项信息 -->
<c:forEach items="${list }" var="customer">
<TR style="FONT-WEIGHT: normal; FONT-STYLE: normal; BACKGROUND-COLOR: white; TEXT-DECORATION: none">
	<TD>${customer.cust_name }</TD>
	<TD>${customer.cust_level.dict_item_name}</TD><!-- 是取得级别里面的名字要.字典 -->
	<TD>${customer.cust_source.dict_item_name }</TD>
	<TD>${customer.cust_industry.dict_item_name }</TD>
	<TD>${customer.cust_address }</TD>
	<TD>${customer.cust_phone }</TD>
	<TD>
	<a href="${pageContext.request.contextPath }/customer/CustomerServlet?method=editCustomerUI&custId=${customer.cust_id}">修改</a>
	&nbsp;&nbsp;
	<a href="${pageContext.request.contextPath }/customer/CustomerServlet?method=removeCustomer&custId=${customer.cust_id}">删除</a>
	</TD>
</TR>	
</c:forEach>

懒加载过滤器 web.xml

<!--   过滤器 懒加载 搜OpenSessionInViewFilter第5版 再在前端请求方法后加 .action -->
  <filter>
  	<filter-name>openSession</filter-name>
  	<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>openSession</filter-name>
  	<url-pattern>*.action</url-pattern>
  </filter-mapping>

遍历客户 下面的分页 上下页 分页 跳转的div

<DIV style="LINE-HEIGHT: 20px; HEIGHT: 20px; TEXT-ALIGN: right">
	共[<B>${totalSize}</B>]条信息,按每页显示
	<select name="pageSize" onchange="changePageSize()"><!-- 点击改变就执行JS方法 -->
	<option value="5" <c:if test="${pageSize==5 }">selected</c:if>>5</option>
	<option value="10" <c:if test="${pageSize==10 }">selected</c:if>>10</option>
	<option value="15" <c:if test="${pageSize==15 }">selected</c:if>>15</option>
	<option value="20" <c:if test="${pageSize==20 }">selected</c:if>>20</option>
	</select>
	条,共[<B>${totalPage}</B>]页 <B>当前第${currentPage}页</B>
	[
	<s:if test="currentPage==1"><!-- 如果是第一页就是第一页文字不可点击 -->
		第一页
	</s:if>
	<s:else>
	<A href="javascript:to_page(${currentPage-1})">到前一页</A><!-- 点击执行js方法 -->
	</s:else>
	][
	<s:if test="currentPage==totalPage"><!-- 如果是总页数就是最后一页文字不可点击 -->
		最后一页
	</s:if>
	<s:else>
	<A href="javascript:to_page(${currentPage+1})">到后一页</A> 
	</s:else>
	]
	<input type="button" value="跳转到第" onclick="to_page()"/>
	<input type="text" size="3" id="page" name="currentPage" />
</DIV>

js

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.11.3.min.js"></script>
<SCRIPT language=javascript>
	function to_page(page){//to_page方法传page参数 如果有参数就改变并提交表单 js版本是1.11.3
		if(page){//如果有参数就执行改变#page标签的页数
			$("#page").val(page);
		}else{//点击转转到 就进入else分支
		var requestPage=$("#page").val();//当前输入的值
		var totalPage="${totalPage}";//总页数
		if (Number(requestPage)>Number(totalPage)) {//如果输入值大于总页 就转到最后一页
			alert("输入的第"+requestPage+"页,超过最大"+totalPage+"页,现到最后一页");
			$("#page").val(totalPage);		}		}
		document.customerForm.submit();}//并提交整个表单到 customer_findByPage.action
		
	function changePageSize(){//改变每页显示条数 的方法
		document.customerForm.submit();	//提交整个表单到findByPage.action
	}
</SCRIPT>

最后都是提交的表单

<FORM id="customerForm" name="customerForm" action="${pageContext.request.contextPath }/customer_findByPage.action" method=post>

筛选

list.jsp

<TABLE cellSpacing=0 cellPadding=2 border=0>
<TBODY>
<TR>
	<TD>客户名称:</TD>
	<TD><s:textfield class="textbox" id="sChannel2" style="WIDTH: 80px" maxLength="50" name="cust_name" /></TD>
	
	<TD>客户来源:</TD>
	<td>
	<select name="cust_source.dict_id" class=textbox id="cust_source" style="WIDTH: 100px;height:21px">
		<option value="">---请选择---</option>
	</select>
	</td>
	<TD>客户行业:</TD>
	<td>
	<select name="cust_industry.dict_id" class=textbox id="cust_industry" style="WIDTH: 100px;height:21px">
		<option value="">---请选择---</option>
	</select>
	</td>
	<TD>客户级别:</TD>
	<td>
	<select name="cust_level.dict_id" class=textbox id="cust_level" style="WIDTH: 100px;height:21px">
		<option value="">---请选择---</option>
	</select>
	</td>
	
	<TD>客户电话:</TD>
	<TD><s:textfield class="textbox" id="sChannel2" style="WIDTH: 80px" maxLength="50" name="cust_phone" /></TD>
	
	<TD><INPUT class=button id=sButton2 type=submit value=" 筛选 " name=sButton2></TD><!-- 点击按钮就提交 -->
	</TR>
</TBODY>
</TABLE>

CustomerAction.java

校验有没有带筛选条件就先用qbc查询出来再分页

public String findByPage() {//分布查询客户列表 两个地方走这个方法左侧客户列表不带条件 筛选带条件
//离线对象如果只是这么创建出来,背后的sql语句 select * from customer;是QBC查询重要组件 能够在dao层之上两层进行查询条件封装 也可以不封装
//只会使用离线对象的两个方法  setXXX(针对聚合查询 总记录数、最大值、最小值、addXXX(针对where条件的设置)
DetachedCriteria criteria=DetachedCriteria.forClass(Customer.class);//离线对象

//判断条件 走筛选的如果带上了条件 就把查询语句 先带上参数查询
if (!StringUtils.isEmpty(customer.getCust_name())) {//校验客户名称 用工具比较客户里名字不为空 就代表前端表单属性name 用模型驱动 传文本框和选择框参数过来了
criteria.add(Restrictions.like("cust_name", "%"+customer.getCust_name()+"%"));}//qbc查询 模糊查询模型驱动获得前端的cust_name作为条件和数据库里查询
if (!StringUtils.isEmpty(customer.getCust_phone())) {//校验客户电话 qbc要加% List list = criteria.add(Restrictions.like("pname", "%肉%")).list();
criteria.add(Restrictions.like("cust_phone", "%"+customer.getCust_phone()+"%"));}

if (customer.getCust_source()!=null&&!StringUtils.isEmpty(customer.getCust_source().getDict_id())) {//校验客户来源里的来源对象不为空并且 里面传过来参数的id值不为空 
	criteria.add(Restrictions.eq("cust_source.dict_id", customer.getCust_source().getDict_id()));}//就把参数进去 以id查询
if (customer.getCust_industry()!=null&&!StringUtils.isEmpty(customer.getCust_industry().getDict_id())) {//校验客户行业里的id值 
	criteria.add(Restrictions.eq("cust_industry.dict_id", customer.getCust_industry().getDict_id()));}
if (customer.getCust_level()!=null&&!StringUtils.isEmpty(customer.getCust_level().getDict_id())) {//校验客户级别里的id值 
	criteria.add(Restrictions.eq("cust_level.dict_id", customer.getCust_level().getDict_id()));}
//并以分页查询
PageBean<Customer> pageBean = customerService.findByPage(criteria , currentPage , pageSize);//查询数据业务 返回pageBean对象
System.out.println(pageBean);
ActionContext.getContext().getValueStack().push(pageBean);//3. 把pageBean存储到作用域 值栈。 push | set | 属性
return Constant.PAGE_SUCCESS;}//返回

js 加载完就执行下拉三个方法

执行的js的 loadDict方法

function loadDict(type_code , tagId , oldVal){//传旧的值进来

<script type="text/javascript">
$(function(){//加载完就 执行三个方法 下拉
		loadDict("001" , "#cust_industry" , "${cust_industry.dict_id}"); //001  -- 客户行业
		loadDict("002" , "#cust_source" , "${cust_source.dict_id}"); //002  -- 客户来源
		loadDict("006" , "#cust_level" , "${cust_level.dict_id}");//006  -- 客户级别
	})
	function loadDict(type_code , tagId , oldVal){//传旧的值进来回显
		//发起请求,获取字典数据 按类型查询字典数据
		var url = "${pageContext.request.contextPath }/baseDict_findByType";
		$.post(url , {"dict_type_code" :type_code } , function(result){
			$(result).each(function(i , n){ //i : 遍历的下标,  n : 遍历出来的对象   字典对象
				$(tagId).append("<option value='"+n.dict_id+"'>"+n.dict_item_name+"</option>")
			});
			//选中该选中的option  在指定的id标签身上找option标签, 按照value值来找,如果找到了就修改这个option标签的属性,修改selected属性,值为selected
			$(tagId).find("option[value='"+oldVal+"']").attr("selected","selected");
		} , "json");	}
</script>

回显筛选的文本框用标记 下拉用上面的js

文本框回显 : 把 <input 换成<s:textfield 并属性值都要打双引号

<TD>客户名称:</TD>
	<TD><s:textfield class="textbox" id="sChannel2" style="WIDTH: 80px" maxLength="50" name="cust_name" /></TD>
<TD>客户电话:</TD>
	<TD><s:textfield class="textbox" id="sChannel2" style="WIDTH: 80px" maxLength="50" name="cust_phone" /></TD>		

信息创建人和对客户负责人

list.jsp 里添加

<TD>信息创建人</TD>
<TD>对客户负责人</TD>

<TD>操作</TD> <!--操作的上面添加-->

<!--遍历的地方添加 并显示 -->
<TD>${customer.cust_create_id.user_name }</TD><TD>${customer.cust_user_id.user_name }</TD>

CustomerAction 里的 添加客户方法save 前先设置创建人

//设置信息创建人和客户负责人 谁负责添加的这个客户谁就是创建人和负责人
User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user");//取到登陆的user
customer.setCust_create_id(user);//设置客户的创建人
customer.setCust_user_id(user);//设置对客户负责人的名字 为登陆创建的user对象
customerService.save(customer);return NONE;

bean包 Customer.java 创建人/负责人String更改为对象user

private User cust_create_id;//信息创建人 改成对象类型 xml也要改
private User cust_user_id; //对客户负责人

Customer.hbm.xml映射关系更改

<!-- <property name="cust_user_id"/>
		<property name="cust_create_id"/> 删除其它字段和持久化类关系 改成多对一的关系 客户和用户-->
		<many-to-one name="cust_create_id" class="com.itheima.bean.User"></many-to-one>
		<many-to-one name="cust_user_id" class="com.itheima.bean.User"></many-to-one>

添加客户提交保存 并重定向到分页查询方法

return Constant.SAVA_SUCCESS;

struts.xml 重定向到分页查询方法

jsp的请求转发(默认)dispatcher 重定向redirect

action的请求转发(默认)chain重定向redirectAction

<result name="save_success" type="redirectAction">customer_findByPage</result>

删除

list.jsp 链接到执行js代码 传id过去

<a href="javascript:del(${customer.cust_id})">删除</a>

js 到delete方法传id过去

function del(id){//删除方法
		var flag=confirm("确定删除这个客户吗");
	if (flag) {//确定就跳转
		location.href="${pageContext.request.contextPath }/customer_delete?cust_id="+id;	}	}

CustomerAction.java建delete方法

public String delete() {//删除客户的方法 传对象
	customerService.delete(customer);
	return Constant.DELETE_SUCCESS;}

CustomerService接口void delete(Customer customer);

CustomerServiceImpl public void delete(Customer customer) { customerDao.delete(customer);}

CustomerDao接口 void delete(Customer customer);

CustomerDaoImpl实现public void delete(Customer customer) {getHibernateTemplate().delete(customer); }

struts.xml 重定向到方法

<result name="delete_success" type="redirectAction">customer_findByPage</result>

修改

list.jsp 传参数过去到edit方法

<a href="${pageContext.request.contextPath }/customer_edit?cust_id=${customer.cust_id}">修改</a>

CustomerAction.java第一步先根据id查询客户对象存值栈 跳转到修改页面

private Customer editCustomer;//属性方法存值栈 声明findById查询出来客户的customer 的变量
public Customer getEditCustomer() {//提供get方法
	return editCustomer;}

public String edit() {//修改客户的方法 第一步先根据id查询客户对象数据跳转到修改页面
	editCustomer = customerService.findById(customer.getCust_id());
	//以属性方法存值栈 把editCustomer成全局变量提供get方法就存到值栈了
	return Constant.EDIT_SUCCESS;}//跳转

CustomerService接口Customer findById(Long cust_id);

CustomerServiceImpl public Customer findById(Long cust_id) {return customerDao.findById(cust_id);}

CustomerDao接口 Customer findById(Long cust_id);

CustomerDaoImpl实现public Customer findById(Long cust_id) { return getHibernateTemplate().get(Customer.class, cust_id);}

struts.xml 转到edit页面

<result name="edit_success">/jsp/customer/edit.jsp</result>

关闭懒加载才能进入edit.jsp Customer.hbm.xml

<many-to-one lazy="false" name="cust_create_id" class="com.itheima.bean.User"></many-to-one>
<many-to-one lazy="false" name="cust_user_id" class="com.itheima.bean.User"></many-to-one>
		
<many-to-one lazy="false" name="cust_source" class="com.itheima.bean.BaseDict"/>
<many-to-one lazy="false" name="cust_industry" class="com.itheima.bean.BaseDict"/>
<many-to-one lazy="false" name="cust_level" class="com.itheima.bean.BaseDict"/>

或者在请求的时候就带 .action

<a href="${pageContext.request.contextPath }/customer_edit.action?cust_id=${customer.cust_id}">修改</a>

回显到 edit.jsp 修改成 值栈里对应的value

<!-- 修改的id的隐藏域  -->
		<input type="hidden" name="cust_id" value="${editCustomer.cust_id}">
		
<!-- 要修改表单项目也 修改成值栈里的 -->
<TABLE cellSpacing=0 cellPadding=5  border=0>
	<TR>
		<td>客户名称:</td>
		<td>
			<INPUT class=textbox id=sChannel2 style="WIDTH: 180px" maxLength=50 name="cust_name" value="${editCustomer.cust_name}">
	</td>
	<td>所属行业 :</td>
	<td>
		<select name="cust_industry.dict_id" class=textbox id="cust_industry" style="WIDTH: 180px;;height:21px">
			<option value="non">---请选择---</option>
		</select>
	</td>
</TR>							
<TR>	
	<td>信息来源 :</td>
	<td>
		<select name="cust_source.dict_id" class=textbox id="cust_source" style="WIDTH: 180px;;height:21px">
			<option value="non">---请选择---</option>
		</select>
	</td>
	<td>客户级别:</td>
	<td>
		<select name="cust_level.dict_id" class=textbox id="cust_level" style="WIDTH: 180px;;height:21px">
			<option value="non">---请选择---</option>
		</select>								
	</td>
</TR>
<TR>
	<td>联系地址 :</td>
	<td>
		<INPUT class=textbox id=sChannel2 style="WIDTH: 180px" maxLength=50 name="cust_address" value="${editCustomer.cust_address}">
	</td>
	<td>联系电话 :</td>
	<td>
		<INPUT class=textbox id=sChannel2 style="WIDTH: 180px" maxLength=50 name="cust_phone" value="${editCustomer.cust_phone}">
		</td>
	</TR>
	<tr>
		<td rowspan=2>
			<INPUT class=button id=sButton2 type=submit value=" 保存 " name=sButton2>
		</td>
	</tr>
</TABLE>

js 设置下拉的回显

<SCRIPT language=javascript>
		$(function(){//加载完就 执行三个方法 下拉
		loadDict("001" , "#cust_industry" , "${editCustomer.cust_industry.dict_id}"); //001  -- 客户行业
		loadDict("002" , "#cust_source" , "${editCustomer.cust_source.dict_id}"); //002  -- 客户来源
		loadDict("006" , "#cust_level" , "${editCustomer.cust_level.dict_id}");//006  -- 客户级别
	})
	function loadDict(type_code , tagId , oldVal){//传旧的值进来回显
		//发起请求,获取字典数据 按类型查询字典数据
		var url = "${pageContext.request.contextPath }/baseDict_findByType";
		$.post(url , {"dict_type_code" :type_code } , function(result){
			$(result).each(function(i , n){ //i : 遍历的下标,  n : 遍历出来的对象   字典对象
				$(tagId).append("<option value='"+n.dict_id+"'>"+n.dict_item_name+"</option>")
			});
			//选中该选中的option  在指定的id标签身上找option标签 按照value值来找,如果找到了就修改这个option标签的属性,修改selected属性,值为selected
			$(tagId).find("option[value='"+oldVal+"']").attr("selected","selected");
		} , "json");	}
</SCRIPT>

保存

edit.jsp

<FORM id=form1 name=form1 action="${pageContext.request.contextPath }/customer_update" method=post>

CustomerAction建update方法

public String update() {//修改客户并保存的方法 修改客户第二步
		customerService.update(customer);
		return Constant.UPDATE_SUCCESS;}//跳转

CustomerService接口void update(Customer customer);

CustomerServiceImpl public void update(Customer customer) { customerDao.update(customer);}

CustomerDao接口 void update(Customer customer);

CustomerDaoImpl实现public void update(Customer customer) {getHibernateTemplate().update(customer); }

struts.xml 重定向到方法

<result name="update_success" type="redirectAction">customer_findByPage</result>

信息创建人和对客户负责人被修改成空了 要在edit.jsp加两个隐藏域

<!-- 信息创建人和对客户负责人的隐藏域  value还是之前的 查出来要被修改对象里面的 对客户负责人里的用户帐号-->
		<input type="hidden" name="cust_user_id.user_id" value="${editCustomer.cust_user_id.user_id}">
		<input type="hidden" name="cust_create_id.user_id" value="${editCustomer.cust_create_id.user_id}">

客户图片回显

Customer.java bean包下的加一个图片字段 image 配置映射

private String cust_image;

Customer.hbm.xml<property name="cust_image"/>

edit.jsp 联系电话下面 添加位置显示图片

<tr>
	<td>客户资质 :</td>
	<td>
		<img src="${pageContext.request.contextPath }/${editCustomer.cust_image}" />
		<input type="file" value="">
	</td>
	</tr>

在增加客户保存之前 把图片地址设置进去

//存储图片
if(upload != null){
	//存储文件 tmp---jpg | png
String fileName  = MyFileUtil.getFileName(uploadFileName);
File file  = new File("D:/heima28/img" , fileName);
FileUtils.copyFile(upload, file);	
//数据库里存放地址设置 
customer.setCust_image("img/"+fileName);
}		
customerService.save(customer);
return Constant.SAVA_SUCCESS;
}

改tomcat配置文件 Servers里面 Tomcat v7.0 Server at localhost-config 配server.xml 真实路径和 项目名虚拟路径

<Context docBase="D:/heima28/img" path="/crm28_522/img"/>

联系人

客户拜访

客户拜访时间日期格式问题:

方式1 转发器utils +xwork-conversion.properties配置${visit.vist_time}

方式2 标签库

BaseDao抽取

联系人和客户 LinkManDaoImpl和CustomerDaoImpl几乎一样

BaseDao.java

package com.itheima.dao;
import java.io.Serializable;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import com.itheima.bean.Customer;
public interface BaseDao<T> {
	void save(T t);
	void delete(T t);
	void update(T t);
	T findById(Serializable id);
	List<T> findAll();
	List<T> findByPage(DetachedCriteria criteria, int currentPage, int pageSize);
	int findCount(DetachedCriteria criteria);
}

BaseDaoImpl.java

package com.itheima.dao.impl;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import com.itheima.dao.BaseDao;

public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> {
	private Class clazz ;//声明
	public BaseDaoImpl(){//有参构造 反射解析T类型
		ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
		clazz = (Class) pt.getActualTypeArguments()[0];
	}
	
	@Override //id
	public T findById(Serializable id) {
		return (T) getHibernateTemplate().get(clazz, id);
	}
	@Override //查全部
	public List<T> findAll() {
		return (List<T>) getHibernateTemplate().findByCriteria(DetachedCriteria.forClass(clazz));
	}
	//=================================================
	@Override //
	public void save(T t) {
		getHibernateTemplate().save(t);
	}
	@Override //
	public void delete(T t) {
		getHibernateTemplate().delete(t);
	}
	@Override //
	public void update(T t) {
		getHibernateTemplate().update(t);		
	}
	@Override //查分页的List集合
	public List<T> findByPage(DetachedCriteria criteria, int currentPage, int pageSize) {
		System.out.println("使用leBaseDaoImpl的findByPage方法~!~");
		criteria.setProjection(null);
		return (List<T>) getHibernateTemplate().findByCriteria(criteria, (currentPage-1)*pageSize , pageSize);
	}
	@Override //查总条数
	public int findCount(DetachedCriteria criteria) {
		criteria.setProjection(Projections.rowCount());
		List<Long> list = (List<Long>) getHibernateTemplate().findByCriteria(criteria);
		if(list.size() > 0 ){
			return list.get(0).intValue();
		}
		return 0;
	}
}

CustomerDao.java 继承BaseDao

public interface CustomerDao extends BaseDao<Customer> {

CustomerDaoImpl.java继承BaseDaoImpl 实现接口CustomerDao

public class CustomerDaoImpl extends BaseDaoImpl<Customer> implements CustomerDao {

登陆拦截器

\itheima\web\interceptor\LoginInterceptor.java

public class LoginInterceptor extends MethodFilterInterceptor{

	@Override
	protected String doIntercept(ActionInvocation arg0) throws Exception {
		
		User user = (User)ServletActionContext.getRequest().getSession().getAttribute("user");
		
		if(user == null){
			System.out.println("没有登录,现在要去执行登录");
			return "login";
		}
		System.out.println("已经登录,放行");
		return arg0.invoke();
	}

struts.xml 在要拦截的package里声明拦截器

<package name="base" extends="json-default" namespace="/">
<!-- 1. 声明拦截器 -->
<interceptors>
	<interceptor name="LoginInterceptor" class="com.itheima.web.interceptor.LoginInterceptor">
	<!-- 不拦的 -->
<param name="excludeMethods">createCode,login</param>
</interceptor>
<!-- 2.  -->	
<interceptor-stack name="baseStack">
	<interceptor-ref name="LoginInterceptor"/>
	<interceptor-ref name="defaultStack"/>
</interceptor-stack>
		</interceptors>
		
		<!-- 2. 定义这个包默认的拦截器是什么 -->
		<default-interceptor-ref name="baseStack"/>
		
		<!-- 3. 配置共性的result -->
		<global-results>
			<result name="login" type="redirect">/login.jsp</result>
		</global-results>
</package>