我是靠谱客的博主 开放眼神,最近开发中收集的这篇文章主要介绍CRM关系管理系统笔记一.CRM项目技术架构介绍二:软件开发的生命周期三:CRM核心业务介绍四:CRM项目所用到的表以及主键字段和表关系简单分析五:项目的规范六:mybatis逆向工程创建数据持久层接口及映射文件七:ssm整合配置八:用户登录功能安全退出的实现九:市场活动功能十:线索模块的功能十一:交易功能的实现十二:CRM用到的插件十三:前端通用代码,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 一.CRM项目技术架构介绍
  • 二:软件开发的生命周期
  • 三:CRM核心业务介绍
  • 四:CRM项目所用到的表以及主键字段和表关系简单分析
      • 1.市场活动表
    • 2.市场活动备注表外键引用市场活动表主键
    • 3.线索表
    • 4.线索备注表
    • 4.线索市场活动关系表
    • 5.联系人表
    • 6.联系人备注表
    • 7.线索市场活动关系表
    • 8.客户表
    • 9.客户关系表
    • 10.字典类型表(下拉列表字段)
    • 11.字段类型具体值表
    • 12.交易表
    • 13.交易备注表
    • 14.交易历史表
    • 15.用户表
    • 主键生成方式
    • 外键与表关系
  • 五:项目的规范
        • 1.web应用根目录下的内容都是不安全的,外界可以通过url直接访问; 所以,一般为了数据的安全,都会把页面放到WEB-INF下,因为WEB-INF目录下的资源是受保护的,外界不能直接访问。(要通过controller层请求转发才可以访问)
        • 2.controller下的方法用public,因为将来是给Springmvc的DispatcherServle这个核心处理器调用的
        • 3.请求转发只能访问服务器内部资源
        • 4.一个资源目录一个controller类
        • 5.@RequestMapping的url要和controller方法所处理完请求之后,响应信息回到的页面的资源目录保持一致,后面加方法名
        • 6.一张表,对应一个mapper对应一个service
        • 7.所有页面都是jsp页面,并且都配置了base 标签等
  • 六:mybatis逆向工程创建数据持久层接口及映射文件
        • 1.添加mybatis逆向工程插件
        • 2.新建一个普通java项目
        • 3.在项目的resources目录下放置配置文件和属性配置文件
  • 七:ssm整合配置
  • 八:用户登录功能安全退出的实现
      • 1.用户访问项目首页,首先进入登录页面。
      • 2.登录页面做登录验证
      • 3.安全退出功能
      • 4.Springmvc拦截器
  • 九:市场活动功能
        • 1.介绍页面切割和模态窗口的概念
        • 2.来到市场活动主页面
        • 2.创建市场活动
        • 3.市场活动的分页查询功能配合分页插件bs_pagination
        • 3.删除市场活动
        • 4.全选按钮的实现
        • 5.修改市场活动
        • 6.批量市场活动(全部导出)
        • 7.选择导出
        • 8.文件导入
            • 技术准备:
        • 9.市场活动明细页面
        • 10.市场活动详细页面的新增备注功能
        • 11.删除市场活动备注功能
        • 12.修改市场活动备注功能
  • 十:线索模块的功能
        • 1.线索页面的显示
        • 2.创建线索的实现
        • 3.线索的分页查询函数实现
        • 4.线索详细页面的显示
        • 5.线索详细页面的查询市场活动
        • 6.实现线索详细页面的市场活动的关联
        • 7.实现线索详细页面的市场活动的删除
        • 8.线索详细页面点击转化按钮到转化页面
        • 9.转化页面的交易里面的市场活动源搜索关联过得市场活动
        • 10.线索详细页面转化按钮的实现
  • 十一:交易功能的实现
        • 1.用户点击菜单的交易来到交易页面
        • 2.交易用户点击创建按钮跳转到创建页面
        • 3.在创建页面点击市场活动源
        • 4.在创建页面点击联系人
        • 5.用户点击阶段直接填入可能性配置
        • 6.创建交易页面的客户自动搜索补全(利用插件bs_typeahead自动补全插件)
        • 7.用户点击保存,保存交易信息
        • 8.查看交易详细信息
        • 9.交易详细页面的图标显示
  • 十二:CRM用到的插件
        • 前端插件使用步骤
        • 1.前端日历插件基于bootstrap框架(datetimepicker)
        • 2.前端分页插件bs_pagination
        • 3.代码搜索自动补全插件bs_typeahead
        • 4.java文件插件Apache-poi文件的创建
        • 5.图表插件echarts
  • 十三:前端通用代码
        • 1.向标签中打入数据
        • 2.全选框选中与取消
        • 3.前端隐藏标签
        • 4.给元素扩展属性
        • 5.父子选择器与和on函数加事件
        • 6.controller返回给前端ajax可以使哪几种数据类型
        • 7.,ajax向后台发送请求时,可以通过data提交参数,data的数据格式有三种格式:

一.CRM项目技术架构介绍

1.视图层(view):展示数据,跟用户交互。
html,css,js,jquery,bootstrap(ext|easyUI),jsp
2.控制层(Controller):控制业务处理流程(接收请求,接收参数,封装参数;根据不同的请求调用业务层处理业务;根据处理结果,返回响应信息)
(servlet,)springMVC(,webwork,struts1,struts2)
3.业务层(Service):处理业务逻辑(处理业务的步骤以及操作的原子性)
JAVASE(工作流:activiti|JBPM)
1,添加学生
2,记录操作日志
4.持久层(Dao/Mapper):操作数据库.
(jdbc,)mybatis(,hibernate,ibatis)

	tbl_table----------pojo

5.整合层:维护类资源,维护数据库资源
spring(IOC,AOP)(,ejb,corba)

二:软件开发的生命周期

1)招标:
投标:----------标书

甲方:
乙方:

2)可行性分析:---------可行性分析报告
技术,经济
3)需求分析:-----------需求文档
产品经理,需求调研
项目原型:容易确定需求,开发项目时作为jsp网页.
4)分析与设计:
架构设计:----------架构文档
物理架构设计:
应用服务器:tomcat(apache),weblogic(bea–>oracle),websphere(ibm),jboss(redhat),resin(MS)
web javaee:13种协议
servlet,jsp,xml,jdbc
mq …
数据库服务器:mysql,oracle,DB2,sqlserver,达梦
逻辑架构设计:代码分层.
视图层–>控制层–>业务层–>持久层–>数据库
技术选型:java,.net
项目设计:---------项目设计文档
物理模型设计:哪些表,哪些字段,字段的类型和长度,以及表和表之间的关系。
powerdesigner-----xxxx.pdm
逻辑模型设计:哪些类,哪些属性和方法,方法的参数和返回值,以及类和类之间关系。
rational rose-----.pdl
界面设计:企业级应用 朴素 -----项目原型
互联网应用 炫酷
算法设计:------算法设计文档
5)搭建开发环境:-----------技术架构文档
创建项目,添加jar包,添加配置文件,添加静态页面,添加公共类以及其它资源;能够正常启动运行。
6)编码实现:-------注释
7)测试:-----------测试用例
8)试运行:---------使用手册
9)上线:-----------实施文档
10)运维:----------运维手册

三:CRM核心业务介绍

1)CRM项目的简介:Customer Relationship Management 客户关系管理系统
企业级应用,传统应用;给销售或者贸易型公司使用,在市场,销售,服务等各个环节中维护客户关系,
CRM项目的宗旨:增加新客户,留住老客户,把已有客户转化为忠诚客户。
2)CRM是一类项目,我们的CRM是给一个大型的进出口贸易公司来使用的,做大宗商品的进出口贸易;商品是受管家管制的。
3)CRM项目的核心业务:
1.系统管理功能:不是直接处理业务数据,为了保证业务管理的功能正常安全运行而设计的功能。用户登录,安全退出,登录验证等给超级管理员,开发和运维人员使用。
2. 业务管理功能:处理业务数据
市场活动:市场部,设计市场活动营销活动
线索:销售部(初级销售),增加线索
客户和联系人:销售部(高级销售),有效地区分和跟踪客户和联系人.
交易:销售部(高级销售),更好地区分和统计交易的各个阶段。
统计图表:管理层,统计交易表中各个阶段数据量。

四:CRM项目所用到的表以及主键字段和表关系简单分析

1.市场活动表

/表: tbl_activity/---------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
owner char(32) utf8_general_ci YES (NULL) select,insert,update,references
name varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
start_date char(10) utf8_general_ci YES (NULL) select,insert,update,references
end_date char(10) utf8_general_ci YES (NULL) select,insert,update,references
cost varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
description varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references

2.市场活动备注表外键引用市场活动表主键

/表: tbl_activity_remark/----------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
note_content varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_flag char(1) utf8_general_ci YES (NULL) select,insert,update,references 0
activity_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

3.线索表

/表: tbl_clue/-----------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
fullname varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
appellation varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
owner char(32) utf8_general_ci YES (NULL) select,insert,update,references
company varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
job varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
email varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
phone varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
website varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
mphone varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
state varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
source varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
description varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
contact_summary varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
next_contact_time char(10) utf8_general_ci YES (NULL) select,insert,update,references
address varchar(255) utf8_general_ci YES (NULL) select,insert,update,references

4.线索备注表

/表: tbl_clue_remark/------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
note_content varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_flag char(1) utf8_general_ci YES (NULL) select,insert,update,references
clue_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

4.线索市场活动关系表

/表: tbl_clue_activity_relation/-----------------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
clue_id char(32) utf8_general_ci YES (NULL) select,insert,update,references
activity_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

5.联系人表

/表: tbl_contacts/---------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
owner char(32) utf8_general_ci YES (NULL) select,insert,update,references
source varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
customer_id char(32) utf8_general_ci YES (NULL) select,insert,update,references
fullname varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
appellation varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
email varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
mphone varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
job varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
description varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
contact_summary varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
next_contact_time char(10) utf8_general_ci YES (NULL) select,insert,update,references
address varchar(255) utf8_general_ci YES (NULL) select,insert,update,references

6.联系人备注表

/表: tbl_contacts_remark/----------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
note_content varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_flag char(1) utf8_general_ci YES (NULL) select,insert,update,references
contacts_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

7.线索市场活动关系表

/表: tbl_contacts_activity_relation/---------------------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
contacts_id char(32) utf8_general_ci YES (NULL) select,insert,update,references
activity_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

8.客户表

/表: tbl_customer/---------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
owner char(32) utf8_general_ci YES (NULL) select,insert,update,references
name varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
website varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
phone varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
contact_summary varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
next_contact_time char(10) utf8_general_ci YES (NULL) select,insert,update,references
description varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
address varchar(255) utf8_general_ci YES (NULL) select,insert,update,references

9.客户关系表

/表: tbl_customer_remark/----------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
note_content varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_flag char(1) utf8_general_ci YES (NULL) select,insert,update,references
customer_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

10.字典类型表(下拉列表字段)

/表: tbl_dic_type/---------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


code varchar(255) utf8_general_ci NO PRI (NULL) select,insert,update,references
name varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
description varchar(255) utf8_general_ci YES (NULL) select,insert,update,references

11.字段类型具体值表

/表: tbl_dic_value/----------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
value varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
text varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
order_no varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
type_code varchar(255) utf8_general_ci YES (NULL) select,insert,update,references

12.交易表

/表: tbl_tran/-----------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
owner char(32) utf8_general_ci YES (NULL) select,insert,update,references
money varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
name varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
expected_date char(10) utf8_general_ci YES (NULL) select,insert,update,references
customer_id char(32) utf8_general_ci YES (NULL) select,insert,update,references
stage varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
type varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
source varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
activity_id char(32) utf8_general_ci YES (NULL) select,insert,update,references
contacts_id char(32) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
description varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
contact_summary varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
next_contact_time char(10) utf8_general_ci YES (NULL) select,insert,update,references

13.交易备注表

/表: tbl_tran_remark/------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
note_content varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_flag char(1) utf8_general_ci YES (NULL) select,insert,update,references
tran_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

14.交易历史表

/表: tbl_tran_history/-------------------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references
stage varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
money varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
expected_date char(10) utf8_general_ci YES (NULL) select,insert,update,references
create_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
tran_id char(32) utf8_general_ci YES (NULL) select,insert,update,references

15.用户表

/表: tbl_user/-----------------

/列信息/-----------

Field Type Collation Null Key Default Extra Privileges Comment


id char(32) utf8_general_ci NO PRI (NULL) select,insert,update,references uuid

login_act varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
name varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
login_pwd varchar(255) utf8_general_ci YES (NULL) select,insert,update,references 密码不能采用明文存储,采用密文,MD5加密之后的数据
email varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
expire_time char(19) utf8_general_ci YES (NULL) select,insert,update,references 失效时间为空的时候表示永不失效,失效时间为2018-10-10 10:10:10,则表示在该时间之前该账户可用。
lock_state char(1) utf8_general_ci YES (NULL) select,insert,update,references 锁定状态为空时表示启用,为0时表示锁定,为1时表示启用。
deptno char(4) utf8_general_ci YES (NULL) select,insert,update,references
allow_ips varchar(255) utf8_general_ci YES (NULL) select,insert,update,references 允许访问的IP为空时表示IP地址永不受限,允许访问的IP可以是一个,也可以是多个,当多个IP地址的时候,采用半角逗号分隔。允许IP是192.168.100.2,表示该用户只能在IP地址为192.168.100.2的机器上使用。
createTime char(19) utf8_general_ci YES (NULL) select,insert,update,references
create_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references
edit_time char(19) utf8_general_ci YES (NULL) select,insert,update,references
edit_by varchar(255) utf8_general_ci YES (NULL) select,insert,update,references

主键生成方式

1)主键字段:在数据库表中,如果有一组字段能够唯一确定一条记录,则可以把它们设计成表的主键字段。
推荐使用一个字段做主键,而且推荐使用没有业务含义的字段做主键,比如:id等。

      主键字段的类型和长度由主键值的生成方式来决定:
               主键值的生成方式:
	       1)自增:借助数据库自身主键生成机制
	               数值型 长度由数据量来决定

		       运行效率低
		       开发效率高
	       2)assighed:程序员手动生成主键值,唯一非空,算法.
	               hi/low:数值型 长度由数据量决定
		       UUID:字符串 长度是32位(这个CRM项目使用)
	       3)共享主键:由另一张表的类型和长度决定
	               tbl_person         tbl_card
		       id     name        id     name
		       1001   zs          1001    card1
		       1002   ls
	       4)联合主键:由多个字段的类型和长度决定

外键与表关系

2)外键字段:用来确定表和表之间的关系。
1)一对多:一张表(A)中的一条记录可以对应另一张表(B)中的多条记录;
另一张表(B)中的一条记录只能对应一张表(A)中的一条记录。

添加数据时,先添加父表记录,再添加子表记录;
删除数据时,先删除子表记录,再删除父表记录;

内连接:查询所有符合条件的数据,并且要求结果在两张表中都有相对应的记录
左外连接:查询左侧表中所有符合条件的数据,即使在右侧表中没有相对应的记录

*如果外键不能为空,优先使用内连接;
如果外键可以为空,
–假如只需要查询那些在另一张表中有相对应的记录,使用内连接
–假如需要查询左侧表中所有符合条件的记录,使用左外连接.
2)一对一:一张表(A)中的一条记录只能对应另一张表(B)中的一条记录;
另一张表(B)中的一条记录也只能对应一张表(A)中的一条记录。

a)共享主键:(不推荐)
添加数据:先添加先产生的表,再后产生的表记录
删除数据:先删除后产生的表记录,再删除先产生的表记录
查询数据:无需进行连接查询

b)唯一外键:
*一对一就是一种特殊的一对多。
*操作跟一对多完全一样。
3)多对多:一张表(A)中的一条记录可以对应另一张表(B)中的多条记录;
另一张表(B)中的一条记录也可以对应一张表(A)中的多条记录。

添加数据时,先添加父表记录,再添加子表记录;
删除数据时,先删除子表记录,再删除父表记录
查询数据时,可能会进行关联查询:

五:项目的规范

1.web应用根目录下的内容都是不安全的,外界可以通过url直接访问; 所以,一般为了数据的安全,都会把页面放到WEB-INF下,因为WEB-INF目录下的资源是受保护的,外界不能直接访问。(要通过controller层请求转发才可以访问)

2.controller下的方法用public,因为将来是给Springmvc的DispatcherServle这个核心处理器调用的

3.请求转发只能访问服务器内部资源

4.一个资源目录一个controller类

5.@RequestMapping的url要和controller方法所处理完请求之后,响应信息回到的页面的资源目录保持一致,后面加方法名

6.一张表,对应一个mapper对应一个service

7.所有页面都是jsp页面,并且都配置了base 标签等

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
	<%String bg=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";%>
<meta charset="UTF-8">
	<base href="<%=bg%>">

六:mybatis逆向工程创建数据持久层接口及映射文件

1.添加mybatis逆向工程插件

<!--myBatis逆向工程插件-->
    <plugin>
	<groupId>org.mybatis.generator</groupId>
	<artifactId>mybatis-generator-maven-plugin</artifactId>
	<version>1.3.2</version>
	<configuration>
	    <verbose>true</verbose>
	    <overwrite>true</overwrite>
	</configuration>
    </plugin>

2.新建一个普通java项目

3.在项目的resources目录下放置配置文件和属性配置文件

这是属性配置文件

jdbc.driverLocation=E:/apache-maven-3.5.0/repository/mysql/mysql-connector-java/5.1.32/mysql-connector-java-5.1.32.jar
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://127.0.0.1:3306/crm2008
jdbc.userId=root
jdbc.password=xxxxxx

这是逆向工程的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

    <!--指定mysql数据库驱动-->
    <!--<classPathEntry location="E://repository-p2p//mysql//mysql-connector-java//5.1.43//mysql-connector-java-5.1.43.jar"/>-->

    <!--导入属性配置-->
    <properties resource="generator.properties"></properties>

    <!--指定特定数据库的jdbc驱动jar包的位置-->
    <classPathEntry location="${jdbc.driverLocation}"/>

    <context id="default" targetRuntime="MyBatis3">

        <!-- optional,旨在创建class时,对注释进行控制,false生成注释,true无注释 -->
        <commentGenerator>
            <property name="suppressDate" value="false"/>
            <property name="suppressAllComments" value="false"/>
        </commentGenerator>

        <!--jdbc的数据库连接 -->
        <jdbcConnection
                driverClass="${jdbc.driverClass}"
                connectionURL="${jdbc.connectionURL}"
                userId="${jdbc.userId}"
                password="${jdbc.password}">
        </jdbcConnection>


        <!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>


        <!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类
            targetPackage     指定生成的model生成所在的包名
            targetProject     指定在该项目下所在的路径|指定生成到的工程名称
        -->
        <javaModelGenerator targetPackage="com.zeng.crm.shi.TranHistory"
                            targetProject="E:/java220729/crm-project/crm/src/main/java">

            <!-- 是否允许子包,即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否对model添加 构造函数 true添加,false不添加-->
            <property name="constructorBased" value="false"/>
            <!-- 是否对类CHAR类型的列的数据进行trim操作 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立的Model对象是否 不可改变  即生成的Model对象不会有 setter方法,只有构造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>

        <!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 -->
        <sqlMapGenerator targetPackage="com.zeng.crm.Mapper.TranHistory"
                         targetProject="E:/java220729/crm-project/crm/src/main/java">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
                type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
                type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象
                type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
        -->
        <javaClientGenerator targetPackage="com.zeng.crm.Mapper.TranHistory"
                             targetProject="E:/java220729/crm-project/crm/src/main/java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

<!--这个是配置数据库中的那张表生成持久层和实体类-->
        <table tableName="tbl_tran_history" domainObjectName="TranHistory"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>

    </context>
</generatorConfiguration>

七:ssm整合配置

配置的pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.zeng</groupId>
  <artifactId>crm</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>crm Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.32</version>
    </dependency>
    <!-- JDBC数据源连接池 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.1</version>
    </dependency>
    <!-- MyBatis框架依赖 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.1</version>
    </dependency>
    <!-- Spring框架依赖的JAR配置 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>4.3.9.RELEASE</version>
    </dependency>
    <!-- Spring AOP支持-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.9</version>
    </dependency>
    <!-- MyBatis与Spring整合依赖 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>
    <!-- servlet及jstl标签库依赖的JAR配置 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp.jstl</groupId>
      <artifactId>jstl-api</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.taglibs</groupId>
      <artifactId>taglibs-standard-spec</artifactId>
      <version>1.2.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.taglibs</groupId>
      <artifactId>taglibs-standard-impl</artifactId>
      <version>1.2.1</version>
    </dependency>

    <!-- 加载jackson插件依赖 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.7.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.7.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.7.3</version>
    </dependency>

    <!--poi依赖  文件保存依赖-->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>3.15</version>
    </dependency>

    <!-- 文件上传 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!-- Log4j2依赖的JAR配置  日志依赖-->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-jcl</artifactId>
      <version>2.3</version>
    </dependency>

  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.*</include>
        </includes>
      </resource>
    </resources>

  </build>
</project>

配置的mybatis核心配置文件

<?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>


    <!--设置日志输出语句,显示相应操作的sql语名-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

</configuration>

配置spring的配置文件
applicationContext_mapper.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置属性配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--配置数据源-->
    <bean id="mysql" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--配置sqlsessionFabean-->
    <bean  class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="mysql"></property>
        <!--配置sqlmapConfig核心配置文件-->
        <property name="configLocation" value="classpath:sqlmapConfig.xml"></property>
        <!--实体类起别名-->
        <property name="typeAliasesPackage" value="com.zeng.crm.shi"></property>
    </bean>
    <!--注册mapper.xml文件-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.zeng.crm.Mapper"></property>
    </bean>
</beans>

配置applicationContext_service.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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--扫描包路径-->
    <context:component-scan base-package="com.zeng.crm.Service"></context:component-scan>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="mysql"></property>
    </bean>

    <!--    添加事务的切面-->
    <tx:advice id="myadvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*select*" read-only="true"/>
            <tx:method name="*find*" read-only="true"/>
            <tx:method name="*get*" read-only="true"/>
            <tx:method name="*search*" read-only="true"/>
            <tx:method name="*insert*" propagation="REQUIRED"/>
            <tx:method name="*save*" propagation="REQUIRED"/>
            <tx:method name="*add*" propagation="REQUIRED"/>
            <tx:method name="*delete*" propagation="REQUIRED"/>
            <tx:method name="*remove*" propagation="REQUIRED"/>
            <tx:method name="*clear*" propagation="REQUIRED"/>
            <tx:method name="*update*" propagation="REQUIRED"/>
            <tx:method name="*modify*" propagation="REQUIRED"/>
            <tx:method name="*change*" propagation="REQUIRED"/>
            <tx:method name="*set*" propagation="REQUIRED"/>
            <tx:method name="*xinsuozhuanhau*" propagation="REQUIRED"></tx:method>
            <tx:method name="*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

    <!--    完成切面和切入点的织入-->
    <aop:config>
        <aop:pointcut id="mypointcut" expression="execution(* com.zeng.crm.Service.*.*.*(..))"/>
        <aop:advisor advice-ref="myadvice" pointcut-ref="mypointcut"></aop:advisor>
    </aop:config>
</beans>

配置springmvc

<?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:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
    <!-- dispatcherServlet截获所有URL请求 -->
    <!--<mvc:DEFAULT-servlet-HANDLER />-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
    <!-- spring mvc 扫描包下的controller -->
    <context:component-scan base-package="com.zeng.crm.controller"/>
    <!-- 配置注解驱动 -->
    <mvc:annotation-driven/>
    <!-- 配置视图解析器 -->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 配置文件上传解析器 id:必须是multipartResolver-->
   <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="#{1024*1024*80}"/>
        <property name="defaultEncoding" value="utf-8"/>
    </bean>

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>  <!--拦截这两开头的controller请求 因为所有cntroller的url都遵循规范-->
            <mvc:mapping path="/settings/**"/>
            <mvc:mapping path="/workbench/**"/>
            <!--不拦截去登录页面 和点击登录验证的请求-->
            <mvc:exclude-mapping path="/settings/qx/user/tolge.do"/>
            <mvc:exclude-mapping path="/settings/qx/user/ypwsfan.do"/>
            <bean class="com.zeng.crm.Interceptor.lanjiedenglu"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

数据库的属性配置文件

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/crm2008?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=

配置web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置中文编码       @Nullable
    private String encoding;
    private boolean forceRequestEncoding;
    private boolean forceResponseEncoding;-->
    <filter>
        <filter-name>zhong</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>zhong</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--配置springmvc框架-->
    <servlet>
        <servlet-name>ss</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup><!--服务器启动就加载-->
    </servlet>

    <servlet-mapping>
        <servlet-name>ss</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>ss</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <!--配置spring的监听器 自启动applicationContext容器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        <!--指定spring的两个配置文件-->
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext_*.xml</param-value>
    </context-param>

    <!--欢迎页面设置-->
    <welcome-file-list>

        <welcome-file>/</welcome-file>

    </welcome-file-list>
</web-app>

八:用户登录功能安全退出的实现

1.用户访问项目首页,首先进入登录页面。

配置欢迎页,在web.xml中

    <!--欢迎页面设置-->
    <welcome-file-list>

        <welcome-file>/</welcome-file>

    </welcome-file-list>

配置欢迎页所设置的路径的controller方法跳转到index.jsp页面

    @RequestMapping("/")
    public String godeng(){
        return "index";
    }
	<script type="text/javascript">/*这里加/项目名   不加不写/开始也行*/
		window.location.href = "<%=request.getContextPath()%>/settings/qx/user/tolge.do";
	</script>

index.jsp页面再用过访问controller层访问登录页面

    /*url要写成完成响应到哪个资源的得目录下   就是响应到的那个资源的目录*/
    @RequestMapping("/settings/qx/user/tolge.do")
    public String tologe(){
        return "settings/qx/user/login";
    }

2.登录页面做登录验证

前端jsp代码

	<%--拿到登录dom对象  得到数据 发送Ajax--%>
	<script>
		$(function (){
			/*在整个页面加载完成之后给一个窗口加键盘按下事件  再通过keycode来判断是不是按下
			* 了Enter键  按下了就发ajax请求  这里可以直接调用登录的点击事件$("#deng").click()无参数 */
			$(window).keydown(function (event){//这里参数代表整个事件源 可以直接获取事件的相关信息
				//alert(event.keyCode)
				if(event.keyCode==13){
					$("#deng").click()
				}
			})

			$("#deng").click(function (){
				var zhanghu = $.trim($("#text").val());
				var mima = $.trim($("#password").val());
				var shitain = $("#checkbox").prop("checked");
				if(zhanghu==""){
					alert("账户不能为空")
					return;
				}
				if(mima==""){
					alert("密码不能为空")
					return;
				}
				$.ajax({
					url:"settings/qx/user/ypwsfan.do" ,
					type:"POST",
					data:{
						loginAct:zhanghu,
						loginPwd:mima,
						idRemPwd:shitain},

					dataType:"json",
					success:function (data){
						if(data.code=="0"){
							$("#msg").html(data.message)
						}
						if(data.code=="1"){
							/*登陆成功跳转页面  因为要请求转发访问私有 所以跳到controller*/
							window.location.href="workbench/index.do";
						}
					},
					beforeSend:function (){
						/*这个函数是在发送ajax请求之前执行的 这个函数的返回值如果是true 则执行玩这个函数就发
						* 如果是false就不发*/
						$("#msg").text("正在加载请稍后.......")
					}

				})
			})
		})
	</script>

后端controller层接收请求代码

    @RequestMapping("/settings/qx/user/ypwsfan.do")
    @ResponseBody  /*ajax请求*/   /*这个参数名要与前端传过来的数据名称一致*/
    public Object ypwsfan(String loginAct, String loginPwd, String idRemPwd,
                          HttpServletResponse response,
                          HttpServletRequest request, HttpSession session){
        /*用户名或者密码错误,用户已过期,用户状态被锁定,ip受限 都不能登录成功*/
        /*1接收参数 2封装参数 3.调用servic不同的方法 4.返回响应信息*/
        Map<String,Object> map=new HashMap<>();
        map.put("loginAct",loginAct);  /*这里的name要与mapper.xml的#{name}一致*/
        map.put("loginPwd",loginPwd);
        User user = UserServic.selectUserLoginActandPwd(map);
        /*返回json用实体类返回自动转json*/
        usercan usercan = new usercan();
        if(user==null){
            //用户民或者密码错误
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("用户民或者密码错误");
        }else{
            String getdate = SimDate.getdate(new Date()); //获取当前时间的字符串
            /*获取请求时的IP*/
            String remoteAddr = request.getRemoteAddr();
            if(getdate.compareTo(user.getExpireTime())>0){
                //过没过期
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("用户民已经过期");
            }
            else if(user.getLockState().equals("0")){
                //用户状态
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("用户已锁定");
            }

            else if(!(user.getAllowIps().contains(remoteAddr))){
                //ip范围
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("用户IP受限");
            }
            //登录成功
            else {
                usercan.setCode(chang.CODE_SUCCESS);
                usercan.setMessage("登陆成功");
                /*登陆成功往session域里面传数据 传整个user 其他页面就可以用了*/
                session.setAttribute(chang.SESSION_NAME,user);
                /*登录成功看是否相中了十天内免登录 选了则创建cookie 没选了则销毁cookie*/
                if(idRemPwd.equals("true")){
                    Cookie username = new Cookie("username", user.getLoginAct());
                    Cookie password = new Cookie("password", user.getLoginPwd());
                    username.setMaxAge(60*60*24*10);
                    password.setMaxAge(60*60*24*10);
                    response.addCookie(username);
                    response.addCookie(password);
                }else {
                    Cookie username = new Cookie("username","1");
                    Cookie password = new Cookie("password", "1");
                    username.setMaxAge(0);  /*销毁浏览器上的同名cookie*/
                    password.setMaxAge(0);
                    response.addCookie(username);
                    response.addCookie(password);/*  首先默认情况下,如果不设置Cookie的path,默认是“/项目名/当前路径的上一层地址”,
                    如:请求路径:/cookie/cookieDome/servlet/login,Cookie的请求路径:/cookie/cookieDome/servlet*/
                }
            }
        }
        return usercan; //会自动转json  因为插件
    }

mapper验证用户名密码是否正确的sql语句

  <select id="queryUserLoginActandPwd" resultMap="BaseResultMap" parameterType="map">
    select <include refid="Base_Column_List"></include>
        from tbl_user
    where login_act=#{loginAct} and login_pwd=#{loginPwd}

  </select>

选中十天内免登录的页面显示

					<div style="width: 350px;">						<%--在这里直接获取浏览器发来的cookie--%>
						<input class="form-control" id="text" type="text" value="${cookie.username.value}"  placeholder="用户名">
					</div>
					<div style="width: 350px; position: relative;top: 20px;">
						<input class="form-control" id="password" type="password" value="${cookie.password.value}" placeholder="密码">
					</div>
					<div class="checkbox"  style="position: relative;top: 30px; left: 10px;">
						<label><%--引入jstl标签库 用if来判断cookie是否为空--%>
							<c:if test="${not empty cookie.username and not empty cookie.password}"
							var="v">
								<input id="checkbox" type="checkbox" checked> 十天内免登录
							</c:if>
							<c:if test="${empty cookie.username and empty cookie.password}"
								  >
								<input id="checkbox" type="checkbox"> 十天内免登录
							</c:if>
						</label>
						&nbsp;&nbsp;
						<span id="msg"></span>
					</div>

3.安全退出功能

用户点击安全退出要清空cookie和销毁session
删除浏览器中的同名cookie

                    Cookie username = new Cookie("username","1");
                    Cookie password = new Cookie("password", "1");
                    username.setMaxAge(0);  /*销毁浏览器上的同名cookie*/
                    password.setMaxAge(0);
                    response.addCookie(username);
                    response.addCookie(password);

销毁session

session.invalidate()

退出完成之后跳转到主页面

   /*处理安全退出的请求*/
    @RequestMapping("/settings/qx/user/tuichu.do")
    public String tuichu(HttpSession session,HttpServletResponse response){
        /*删除cookie 销毁session 返回登录页面*/
        Cookie username = new Cookie("username","1");
        Cookie password = new Cookie("password", "1");
        username.setMaxAge(0);  /*销毁浏览器上的同名cookie*/
        password.setMaxAge(0);
        response.addCookie(username);
        response.addCookie(password);/*  首先默认情况下,如果不设置Cookie的path,默认是“/项目名/当前路径的上一层地址”,
                    如:请求路径:/cookie/cookieDome/servlet/login,Cookie的请求路径:/cookie/cookieDome/servlet*/
        session.invalidate(); //直接销毁服务器端session

        return "redirect:/";  //这里要用重定向  借助springmvc来重定向的 所以不要加/项目名 低层执行response.send...

    }

4.Springmvc拦截器

拦截器:
a)提供拦截器类:implements HandlerInterceptor{
–pre
–post
–after
}


public class lanjiedenglu implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        /*在请求被处理前执行这个拦截  判断是否登录过 session里有没有数据*/
        HttpSession session = httpServletRequest.getSession();

        User user = (User)session.getAttribute(chang.SESSION_NAME);
        if(user==null){
            /*说明用户没有登录过  拦截 跳到登录页面*/
            httpServletResponse.sendRedirect("/crm");
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
 b)配置拦截器:springmvc.xml
    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>  <!--拦截这两开头的controller请求 因为所有cntroller的url都遵循规范-->
            <mvc:mapping path="/settings/**"/>
            <mvc:mapping path="/workbench/**"/>
            <!--不拦截去登录页面 和点击登录验证的请求-->
            <mvc:exclude-mapping path="/settings/qx/user/tolge.do"/>
            <mvc:exclude-mapping path="/settings/qx/user/ypwsfan.do"/>
            <bean class="com.zeng.crm.Interceptor.lanjiedenglu"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

九:市场活动功能

1.介绍页面切割和模态窗口的概念

<div>和<iframe>:
<div>:切割页面。
        <div style="height:10%;width=20%">
 <iframe>:显示页面。
        <div style="height:10%;width=20%">
	<iframe href="url">可以起name
    </div>
    window.open("打开网页的地址","把网页打开到哪个iframe里面  name属性")

模态窗口:模拟的窗口, 本质上是div,通过设置z-index大小来实现的;
初始时,z-index初始参数是<0,所以不显示;
需要显示时,z-index值设置成>0即可。
bootstrap前端框架来控制z-index的大小。
控制模态窗口的显示与隐藏:
1)方式一:通过标签的属性data-toggle=“modal” data-target=“模态窗口的id”
2)方式二:通过js函数控制:
选择器(选中div).modal(“show”);//显示选中的模态窗口
选择器(选中div).modal(“hide”);//关闭选中的模态窗口
3)方式三:通过标签的属性data-dismiss=“modal”
点击添加了data-dismiss=“modal"属性的标签,自动关闭该标签所在的模态窗口。
模态窗口的意义:
window.open(“url”,”_blank");
模态窗口本质上就是原来页面中的一个div,只有一个页面;所有的操作都是在同一个页面中完成。

2.来到市场活动主页面

用户点击市场活动,发同步请求,跳到市场活动的主页面

				<li class="liClass"><a href="workbench/activity/AllUsers.do" target="workareaFrame"><span class="glyphicon glyphicon-play-circle"></span> 市场活动</a></li>

这个接收请求的controller还查询市场活动主页面所需要的数据,调用service层查询

    @RequestMapping("/workbench/activity/AllUsers.do")
    public String AllUsers(HttpServletRequest request){
        /*查到所有用户 把数据方入request域中  请求转发到/activity/index.jsp页面*/
        List<User> users = servic.selectAllUsers();
        request.setAttribute("users",users);

        return "workbench/activity/index";
    }

把查询到的结果通过Request域请求转发到主页面

主页面从域中取出数据显示到页面

								<select class="form-control" id="create-marketActivityOwner">
									<c:forEach items="${users}" var="u">
										<option value="${u.id}">${u.name}</option>
									</c:forEach>
								</select>

2.创建市场活动

用户点击创建,弹出创建市场活动的模态窗口

		$("#chuang").click(function (){
			/*点击创建就让这个模态窗口中的from表单重置数据  用dom对象的方法*/
			$("#chuangmo").get(0).reset()

			/*给创建按钮加一个单击事件  点击则显示模态窗口*/
			$("#createActivityModal").modal("show")
		})

用户填写表单,点击保存发送ajax异步请求,收集参数,所有用户填写的参数发给后端

		/*发ajax请求*/
		$("#baocun").click(function (){

			/*表单验证 先获得参数*/
			var owner = $("#create-marketActivityOwner").val();//所有者的iD
			var name = $("#create-marketActivityName").val();//活动名
			var startDate = $("#create-startTime").val();//开始日期
			var endDate = $("#create-endTime").val();//结束日期
			var cost = $("#create-cost").val();//成本
			var description = $("#create-describe").val();//描述
			if(owner==""){
				alert("所有者不能为空")
				return;
			}
			if(name==""){
				alert("活动名不能为空")
				return;
			}
			if(startDate!="" && endDate!=""){
				if(startDate>=endDate){
					alert("日期填写错误")
					return;
				}
			}
			var zhengze=/^(([1-9]d*)|0)$/;//定义一个正则表达式
			if(!zhengze.test(cost)){  //用正则表达式.test来验证cost
				alert("成本只能是非负整数")
				return;
			}

			$.ajax({
				url:"workbench/activity/saveCreateActivity.do",
				data:{
					owner:owner,
					name:name,
					startDate:startDate,
					endDate:endDate,
					cost:cost,
					description:description
				},
				dataType:"json",
				type:"POST",
				success:function (data) {
					if(data.code=="1"){
						//关闭模态窗口
						$("#createActivityModal").modal("hide")
						//刷新市场活动列,显示第一页数据,保持每页显示条数不变(到时候在写)
						queryhuodong(1,$("#fenye").bs_pagination('getOption','rowsPerPage'))
						alert("添加成功")

					}
					if(data.code=="0"){
						$("#createActivityModal").modal("show")
						//显示错误信息 保持模态窗口
						alert(data.message)

					}
				}
			})

		})

在controller里接收参数,封装参数为市场活动实体类,调用service层方法,返回响应信息是否增加成功

 /*添加市场活动*/
    @RequestMapping("/workbench/activity/saveCreateActivity.do")
    @ResponseBody                   /*用springmvc实体类拿到参数*/
    public Object saveCreateActivity(Activity activity, HttpSession session){
        /*id createTime创建时间  createBy由谁创建应该是当前用户是谁就谁创建的session  */
        activity.setId(UUIDsaner.getUUID());//UUID生成id32位
        activity.setCreateTime(SimDate.getdate(new Date())); //取得当前时间转化为字符串
        User user= (User) session.getAttribute(chang.SESSION_NAME);
        activity.setCreateBy(user.getId());  //这个是由user的ID
        /*需要先处理看业务逻辑层有没有异常*/
        usercan usercan = new usercan();
        try {
            int i = actyServoce.insertActivity(activity);
            if(i!=0){
                usercan.setCode(chang.CODE_SUCCESS);
                usercan.setMessage("添加成功");
            }else {
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("出现网络繁忙请稍后重试。。。");
            }
        }catch(Exception e){
            e.printStackTrace();//打印异常信息
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("出现网络繁忙请稍后重试。。。");
        }

        return usercan;
    }

在ajax回调函数里面,如果响应信息是成功则关闭窗口,刷新局部页面,如果失败则不关闭模态窗口,但要提示错误信息

3.市场活动的分页查询功能配合分页插件bs_pagination

用户一点击市场活动,来到主页面,在<script 里面就要分页查询市场活动的记录条数显示到页面,发送的是ajax异步请求,要收集参数,按条件查询的参数,但这些条件在sql语句中只能是动态sql,还要每页显示条数和页号作为参数
前端查询函数

	/*要把beginNo 和 pagesize定义成函数的参数 应为这两个是要根据用户点动态改变的*/
function queryhuodong(beginNo,pagesize){
	/*这里发查询的ajax请求*/
	//先获取数据
	var name = $("#mingcheng").val();
	var owner = $("#suoyouzhe").val();
	var startDate = $("#startTime").val();
	var endDate = $("#endTime").val();
	var beginNo=beginNo;  /*初始的时候是1 每页显示10页*/
	var pagesize=pagesize;
	//发ajax请求
	$.ajax({
		url:"workbench/activity/queryzong.do",
		data:{
			name:name,
			owner:owner,
			startDate:startDate,
			endDate:endDate,
			beginNo:beginNo,
			pagesize:pagesize
		},
		type:"POST",
		dataType: "json",
		success:function (data){
			/*执行这个拼接字符串 打入表单*/

			var html='';
			$.each(data.activities,function (i,actv){
				html+="<tr class="active">"        /*这里的复选框是放的活动的id*/
				html+="<td><input type="checkbox" value='"+actv.id+"' /></td>"   /*这里改这个超链接 传参数id*/
				html+="<td><a style="text-decoration: none; cursor: pointer;" οnclick="window.location.href='workbench/activity/selectActivityandbeizhu.do?id="+actv.id+"'">"+actv.name+"</a></td>"
				html+="<td>"+actv.owner+"</td>"
				html+="<td>"+actv.startDate+"</td>"
				html+="<td>"+actv.endDate+"</td>"
				html+="</tr>"
			})
			$("#huodongbiao").html(html)
            /*在每次刷新数据的时候 都把全选设置为false*/
            $("#quanxuan").prop('checked',false);

			/*计算总页数*/
			var zongye=1;/*如果总记录条数摸每页显示条数 不为0 就是总记录条数除每页显示条数取整数加一*/
			if(data.zongtiao%pagesize==0){
				zongye=data.zongtiao/pagesize;
			}else {/*js的系统函数 parseInt()将这个数取整数部分*/
				zongye=parseInt(data.zongtiao/pagesize)+1;
			}
			/*要在这个函数的ajax的回掉函数里加插件查询函数  应为要获取参数 每页条数和总页数和总记录条数  而且
			* 每发一次请求这个插件的信息要跟着变*/
			$("#fenye").bs_pagination({
				currentPage:beginNo,//当前页号 参数传
				totalRows:data.zongtiao,//总记录条数 ajax返回了
				totalPages:zongye, //总页数是这个函数的必填值 是根据总记录条数 每页显示条数算出来的
				rowsPerPage:pagesize,//每页显示条数
				visiblePageLinks: 5,//这个是那个卡片数最多5个
				showGoToPage: true,//显示跳转部分  默认true显示
				showRowsPerPage: true,//显示设置每页条数 默认true显示
				showRowsInfo: true,//显示记录的信息部分 默认true显示
				onChangePage:function (event,objpage) {
					/*这个是每次页号发生改变就会执行这个函数 第一个参数是这个事件源
					* 第二个参数是这个 页面对象 包含的是外面的函数发生页号改变的所有属性信息
					* 页号发生改变 上面的属性值也会跟着变*/
					//当页号发生改变是 再次调用这个函数 去打请求刷新这个页面 把改变后的当前页号和每页显示条数传给他
					queryhuodong(objpage.currentPage,objpage.rowsPerPage);

				}
			})
		}
	})
}

controller层代码

  /*按条件查 市场活动*/
    @RequestMapping("/workbench/activity/queryzong.do")
    @ResponseBody               /*这个参数要和前端传过来的参数名一致*/
    public Object queryzong(String name,String owner,String startDate,
                            String endDate,int beginNo,int pagesize){
        /*封装参数  map的key要和mapper.xml的#{name}一致*/
        Map<String,Object> map=new HashMap<>();
        map.put("name",name);
        map.put("owner",owner);   /*这里的前四个条件时可以为空的 应为用了动态sql*/
        map.put("startDate",startDate);
        map.put("endDate",endDate);
        map.put("beginNo",(beginNo-1)*pagesize);  //分页公式
        map.put("pagesize",pagesize);
        /*调用业务逻辑层返回数据*/
        List<Activity> activities = actyServoce.selectActivityByConditionForPage(map);
        int i = actyServoce.queryCountOfActivityByzong(map);
        /*利用map封装参数*/
        Map<String,Object> map1=new HashMap<>();
        map1.put("activities",activities);
        map1.put("zongtiao",i);
        /*返回数据*/
        return map1;
    }

查询sql语句

 <select id="selectActivityByConditionForPage" parameterType="map" resultMap="BaseResultMap">
    select a.id,u1.name as owner,a.name,a.start_date,a.end_date,
           a.cost,a.description,a.create_time,u2.name as create_by,
           a.edit_time,u3.name as edit_by
    from tbl_activity as a
    join tbl_user as u1 on a.owner=u1.id
    join tbl_user as u2 on a.create_by=u2.id
    left join  tbl_user as u3 on a.edit_by=u3.id
    <where>
      <if test="name!=null and name!=''">
        and a.name like concat('%',#{name},'%')
      </if>
      <if test="owner!=null and owner!=''">
        and u1.name like concat('%',#{owner},'%')
      </if>
      <if test="startDate!=null and startDate!=''">
        and a.start_date >= #{startDate}
      </if>
      <if test="endDate!=null and endDate!=''">
        and a.end_date &lt;= #{endDate}
      </if>
    </where>
    order by a.create_time desc
    limit #{beginNo},#{pagesize}
  </select>

  <select id="queryCountOfActivityByzong" resultType="int" parameterType="map">
    select count(*)
    from tbl_activity as a
    join tbl_user as u1 on a.owner=u1.id
    join tbl_user as u2 on a.create_by=u2.id
    left join  tbl_user as u3 on a.edit_by=u3.id
    <where>
      <if test="name!=null and name!=''">
        and a.name like concat('%',#{name},'%')
      </if>
      <if test="owner!=null and owner!=''">
        and u1.name like concat('%',#{owner},'%')
      </if>
      <if test="startDate!=null and startDate!=''">
        and a.start_date >= #{startDate}
      </if>
      <if test="endDate!=null and endDate!=''">
        and a.end_date &lt;= #{endDate}
      </if>
    </where>

  </select>

在jsp页面中直接调用这个查询函数

		/*首先进入页面 默认显示第一页 和每页显示十条*/
		queryhuodong(1,10);

3.删除市场活动

用户选择要删除的市场活动,点击删除,发送jajx异步请求,把选中的市场活动id全部传给controller层数组

$("#shanchu").click(function (){

				/*收集参数 发ajax请求*/
				var s=$("#huodongbiao input[type='checkbox']:checked");
				if(s.size()==0){
					alert("请选择要删除的数据")
					return
				}
			if(window.confirm("确认要删除吗,,,不可恢复哈")){
				var str="";
				/*遍历这个被选中的数组 然后做字符串的拼接  id=name1&id=name2*/
				$.each(s,function (){  //点value是获取属性值

					str+="id="+this.value+"&"
				})
				str=str.substr(0,str.length-1)

				$.ajax({
					url:"workbench/activity/deletehuodong.do",
					data:str,
					dataType:'json',
					type:"POST",
					success:function (data){
						if(data.code=="1"){
							queryhuodong($("#fenye").bs_pagination('getOption','currentPage'),$("#fenye").bs_pagination('getOption','rowsPerPage'))
						}
						if(data.code=="0"){
							alert(data.message)
						}
					}
				})
			}
		})

在controller层用string[] 数组来接收同一参数名不同参数值的数据,

 /*删除市场活动*/
    @RequestMapping("/workbench/activity/deletehuodong.do")
    @ResponseBody               //这里的入参是字符串数组 参数名要和前端的参数一
                                //前端的参数用id=name&id=name2的形式传过来
    public Object deletehuodong(String[] id){
        usercan usercan = new usercan();
        /*只要是增删改都要try业务逻辑层有没有异常*/
        try {
           int i = actyServoce.deleteActivityByIds(id);
           if(i>0){
               usercan.setCode(chang.CODE_SUCCESS);
           }else {
               usercan.setCode(chang.CODE_SHIBAI);
               usercan.setMessage("系统异常请稍后");
           }
       }catch (Exception e){
           e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("系统异常请稍后");
       }
      return usercan;

    }

调用mapper层批量删除用Foreach标签

  <delete id="deleteActivityByIds" parameterType="String">
    delete from tbl_activity where id in
    <foreach collection="array" item="id" separator="," open="(" close=")">
      #{id}      /*这里的collection有三个参数   item是代表了遍历出来的一个对象 */
    </foreach>
  </delete>

controller返回响应信息成功或者失败

在页面端成功就调用查询函数刷新,不成功就提示错误信息

4.全选按钮的实现

事件函数里的this带表当前正在发生这个事件的dom对象

<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>

        /*给全选按钮加单击事件 全选列表则全选  这种加事件的方式只能给固有元素加事件
        *   固有元素:当调用事件函数给元素添加事件时,如果元素已经生成,则这些元素叫做固有元素;*/
        $("#quanxuan").click(function (){
            /*全选的checked属性是true则 列表的checked属性值也是true*/
            /*获取全部的列表 父子选择器 中间是空格 则可以筛选所有的子标签 中间是>则只能筛选一级字标签 【属性过滤】
            * this代表当前事件元素的dom对象 */
            $("#huodongbiao input[type='checkbox']").prop('checked',this.checked)
        })
        /*给所有列表元素加单击事件  只要有一行没被选中 则全选变暗 如果都全部手动选中 则全选变亮*/
        /*这里因为链表是动态生成的 ajax异步请求不用等待排队 所以当加单击事件时 列表元素还没有生成 所有要用另一种
        * jquery给元素加事件的方式 这种方式既可以给固有元素加也可以给动态生成的元素加*/
        $("#huodongbiao").on('click',"input[type='checkbox']",function () {
            /* 父元素:必须是固有元素,可以直接父元素,也可以是间接父元素.
            * 事件类型  子选择器  函数*/
            //可以获取列表的长度 与列表中被选中的列表长度作比较 如果相等说明全都选中了 反之没有
           if($("#huodongbiao input[type='checkbox']").size()==$("#huodongbiao input[type='checkbox']:checked").size()) {
               $("#quanxuan").prop('checked',true);
           }else{
               $("#quanxuan").prop('checked',false);
           }
        })

5.修改市场活动

用户在市场活动主页面,选择要修改的市场活动,点击"修改"按钮,弹出修改市场活动的模态窗口;
用户在修改市场活动的模态窗口填写表单,点击"更新"按钮,完成修改市场活动的功能.
*每次能且只能修改一条市场活动
*所有者 动态的
*表单验证(同创建)
*修改成功之后,关闭模态窗口,刷新市场活动列表,保持页号和每页显示条数都不变
*修改失败,提示信息,模态窗口不关闭,列表也不刷新

大概步骤
用户点击修改,先发送ajax请求,参数为市场活动的id,去查询这个市场活动的全部信息

		/*给修改家单击事件  获取参数 发ajax请求 把响应的数据射到相应的框框中*/
		$("#xiugai").click(function (){
		var s=	$("#huodongbiao input[type='checkbox']:checked");
		/*只能选中一个  其他都不行*/
			if(s.size()==0){
				alert("请选中一个要修改的数据")
				return
			}
			if(s.size()>1){
				alert("一次只能修改一个数据")
				return
			}
		/*获取选中的option的value*/
			var id=s.val();

			$.ajax({
				url:"workbench/activity/selectAllByIdActivity.do",
				data: {id},
				dataType:"json",
				type:"POST",
				success:function (data){
					/*把所有者的(用户)id射到select标签的value里 就会自动选中与这个value相等的option*/
					$("#edit-marketActivityOwner").val(data.owner)
					/*要把市场活动的id给一个隐藏的标签里面 修改点击保存发请求要用*/
					$("#shichangid").val(data.id)
					$("#edit-marketActivityName").val(data.name)
					$("#edit-startTime").val(data.startDate)
					$("#edit-endTime").val(data.endDate)
					$("#edit-cost").val(data.cost)
					$("#edit-describe").val(data.description)
					/*打开模态窗口*/
					$("#editActivityModal").modal("show")
				}
			})
		})

controller层接收到参数,去调用查返回市场活动的实体类对象,return返回


    /*点击修改发请求显示这条点击了修改的数据*/
    @RequestMapping("/workbench/activity/selectAllByIdActivity.do")
    @ResponseBody
    public Object selectAllByIdActivity(String id){
        /*封装参数  调用业务逻辑层 响应信息*/
        Activity activity = actyServoce.selectAllById(id);
        return activity;
    }

前端在修改市场活动的模态窗口先显示查到的数据


		/*给更新加单击事件*/
		$("#gengxin").click(function (){
			/*获取参数*/
		    var owner=$("#edit-marketActivityOwner").val()  //列表的value就是所有者的value
			/*要把市场活动的id给一个隐藏的标签里面 修改点击保存发请求要用*/
			var id=$("#shichangid").val()
			var name= $("#edit-marketActivityName").val()
			var startDate= $("#edit-startTime").val()
			var endDate=$("#edit-endTime").val()
			var cost=$("#edit-cost").val()
			var description=$("#edit-describe").val()
			/*表单验证  日期大小 代价非负整数 名称所有者不能为空*/
			if(owner==""){
				alert("所有者不能为空")
				return;
			}
			if(name==""){
				alert("活动名不能为空")
				return;
			}
			if(startDate!="" && endDate!=""){
				if(startDate>=endDate){
					alert("日期填写错误")
					return;
				}
			}
			var zhengze=/^(([1-9]d*)|0)$/;
			if(!zhengze.test(cost)){
				alert("只能为非负整数")
				return
			}
			$.ajax({
				url:"workbench/activity/updateAcyivityById.do",
				data:{
					id:id,
					owner:owner,
					name:name,
					startDate:startDate,
					endDate:endDate,
					cost:cost,
					description:description
				},
				dataType:"json",
				type:"POST",
				success:function (data){
					if(data.code=="1"){
						/*如果更新成功当前页号和每页显示条数不变 关闭模态窗口*/
						queryhuodong($("#fenye").bs_pagination('getOption','currentPage'),$("#fenye").bs_pagination('getOption','rowsPerPage'))
						$("#editActivityModal").modal("hide")
					}else if(data.code=="0"){
						alert(data.message)
						$("#editActivityModal").modal("show")
					}
				}
			})
		})

显示数据再用户更新完数据之后,用户点击更新,参数为修改市场活动中表单中的参数

controller层收到参数封装为市场活动的实体类对象,调用方法保存这个市场活动,返回响应信息就是成功或者失败

  /*点击修改 获取参数 前端只能传7个参数 还有修改时间和修改人自己写 session里获取*/
    @RequestMapping("/workbench/activity/updateAcyivityById.do")
    @ResponseBody
    public Object updateAcyivityById(Activity activity,HttpSession session){
        /*获取参数  封装参数  还有要有修改时间和修改人*/
        String getdate = SimDate.getdate(new Date());
       User user= (User) session.getAttribute(chang.SESSION_NAME);
        String id = user.getId(); /*存的也是修改人的id*/
        activity.setEditTime(getdate);
        activity.setEditBy(id);
        usercan usercan = new usercan();//用来封装响应信息的实体类
        try {
         int i=   actyServoce.updateActivityById(activity);
         if(i==1){
             usercan.setCode(chang.CODE_SUCCESS);
         }else {
             usercan.setCode(chang.CODE_SHIBAI);
             usercan.setMessage("当前系统繁忙请稍后重试");
         }
        }catch (Exception e){
            e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("当前系统繁忙请稍后重试");
        }
      return usercan;
    }

在前端成功就是刷新页面,失败就提示错误信息

6.批量市场活动(全部导出)

技术准备:
使用apache-poi插件生成excel文件
技术准备:
1)使用java生成excel文件:apache-poi

     关于办公文档插件使用的基本思想:把办公文档的所有元素封装成普通的Java类,
                                 程序员通过操作这些类达到操作办公文档目的。
 文件---------HSSFWorkbook
 页-----------HSSFSheet
 行-----------HSSFRow
     列-----------HSSFCell
 样式---------HSSFCellStyle

 使用apache-poi生成excel:
     a)添加依赖:
            <dependency>
	      <groupId>org.apache.poi</groupId>
	      <artifactId>poi</artifactId>
	      <version>3.15</version>
	    </dependency>
     b)使用封装类生成excel文件:
       
   2)文件下载:
     filedownloadtest.jsp
 ActivityController
     |->fileDownload()

 *所有文件下载的请求只能发送同步请求。
		/*给批量下载按钮加单击事件 发同步请求  因为文件下载只能是同步请求*/
		$("#exportActivityAllBtn").click(function (){
			window.location.href="workbench/activity/selectAll.do";
		})

在controller层使用这个插件生成excel文件

 /*这个是处理用户点击批量导出的请求  应为是响应excel文件
    * 所以是手动响应到浏览器 不要返回值*/
    @RequestMapping("/workbench/activity/selectAll.do")
    public void selectAll(HttpServletResponse response) throws IOException {
        /*设置响应类型 为二进制文件*/
        response.setContentType("application/octet-stream;charset=UTF-8");
        /*获取字节输出流*/
        ServletOutputStream outputStream = response.getOutputStream();
        /*拿到市场活动的信息 先下载到服务器磁盘 这里本机充当服务器*/
        List<Activity> activities = actyServoce.selectAll();
        /*使用apache-poi插件生成excel文件  要先加依赖*/
        /*创建一个文件类  往里面加页*/
        HSSFWorkbook wb = new HSSFWorkbook();
        /*a往页里加行*/
        HSSFSheet sheet = wb.createSheet("市场活动");
        /*0代表第一行*/
        HSSFRow row = sheet.createRow(0);
        /*往行里加列*/
        HSSFCell cell = row.createCell(0);
        /*给0行0列设置值*/
        cell.setCellValue("id");
        cell=row.createCell(1);/*设置第二列*/
        cell.setCellValue("所有者");
        cell=row.createCell(2);
        cell.setCellValue("名称");
        cell=row.createCell(3);
        cell.setCellValue("开始时间");
        cell=row.createCell(4);
        cell.setCellValue("结束时间");
        cell=row.createCell(5);
        cell.setCellValue("成本");
        cell=row.createCell(6);
        cell.setCellValue("描述信息");
        cell=row.createCell(7);
        cell.setCellValue("创建时间");
        cell=row.createCell(8);
        cell.setCellValue("创建人");
        cell=row.createCell(9);
        cell.setCellValue("修改时间");
        cell=row.createCell(10);
        cell.setCellValue("修改人");
        /*表头设置好了 设置表的身体 遍历list集合*/
        for(int i=0;i<activities.size();i++){
            /*查出来几行就要设置几行*/
           row= sheet.createRow(i+1);
           cell = row.createCell(0);
            /*给0行0列设置值*/
            cell.setCellValue(activities.get(i).getId());
            cell=row.createCell(1);/*设置第二列*/
            cell.setCellValue(activities.get(i).getOwner());
            cell=row.createCell(2);
            cell.setCellValue(activities.get(i).getName());
            cell=row.createCell(3);
            cell.setCellValue(activities.get(i).getStartDate());
            cell=row.createCell(4);
            cell.setCellValue(activities.get(i).getEndDate());
            cell=row.createCell(5);
            cell.setCellValue(activities.get(i).getCost());
            cell=row.createCell(6);
            cell.setCellValue(activities.get(i).getDescription());
            cell=row.createCell(7);
            cell.setCellValue(activities.get(i).getCreateTime());
            cell=row.createCell(8);
            cell.setCellValue(activities.get(i).getCreateBy());
            cell=row.createCell(9);
            cell.setCellValue(activities.get(i).getEditTime());
            cell=row.createCell(10);
            cell.setCellValue(activities.get(i).getEditBy());
        }
        /*把excel文件输出到服务器*/
        /*OutputStream fos = new FileOutputStream("E:\aa\shichanghuodong.xls");
        wb.write(fos);*/
        /*刷新流 关闭流 */
        /*fos.flush();      //这种先写到磁盘 从磁盘读 再写到磁盘效率很低  直接让wb.write写到浏览器输出流 就不要先写到服务器磁盘中
        fos.close();*/


        /*这里就是处理下载  从服务器里下载*/
        /*先把文件从磁盘读到内存 再写*/

/*
        FileInputStream fileInputStream = new FileInputStream("E:\aa\shichanghuodong.xls");
        byte[] bytes=new byte[256]; //读到字节数组中在从字节数组写出
        int len=0;  *//*这个read是返回读到字节数组中的长度  没有了就返回负一*//*
        while ((len=fileInputStream.read(bytes))!=-1){
            *//*响应到浏览器*//*
            outputStream.write(bytes,0,len);
        }*/
        /*应为浏览器默认会自动把响应信息显示到窗口  但我们要弹出下载框 所以要设置响应头信息告诉浏览器 filename=shichang.xls默认下载时的文件名*/
        response.addHeader("content-Disposition","attachment;filename=shichang.xls");
        wb.write(outputStream);
        wb.close();
    }

查询的sql

  <select id="selectAll" resultMap="BaseResultMap">
    select a.id,u1.name as owner,a.name,a.start_date,a.end_date,
           a.cost,a.description,a.create_time,u2.name as create_by,
           a.edit_time,u3.name as edit_by
    from tbl_activity as a
           join tbl_user as u1 on a.owner=u1.id
           join tbl_user as u2 on a.create_by=u2.id
           left join  tbl_user as u3 on a.edit_by=u3.id
    order by a.create_time desc
  </select>

7.选择导出

只有前端要传递要导出的市场活动的id参数,同名不同值的id参数。然后controller层用的是String[]数组接收

		/*给选择导出按钮加单击事件  收集参数 用ajax发同步请求 async设置为false*/
		$("#exportActivityXzBtn").click(function (){
			/*获取全部选中的列表  遍历拼接id 格式为id=v1&id=v2*/
		var Xzarray=$("#huodongbiao input[type='checkbox']:checked");
		/*表单验证  为空则提示用户要选至少一个*/
			if(Xzarray.size()==0){
				alert("请至少选一个")
				return
			}
		var id='';
			$.each(Xzarray,function (){
				/*this代表每次遍历出来的对象*/
				id+="id="+this.value+"&"
			})
			/*截取字符串*/
			id=id.substr(0,id.length-1)
			/*发ajax同步请求  不可取*/
/*			$.ajax({
				url:"workbench/activity/selectByIds.do",
				async:false,
				data:id,
				type:"POST"

			})*/
			window.location.href="workbench/activity/selectByIds.do?"+id+""

		})

controller层:

    /*根据用户提交过来的id数组  查出来市场活动信息 再通过
    * apache-poi插件写到HSSFwb中 再手动响应给浏览器*/
    @RequestMapping("/workbench/activity/selectByIds.do")
    public void selectByIds(String[] id,HttpServletResponse response) throws IOException {
        /*设置响应类型 为二进制文件*/
        response.setContentType("application/octet-stream;charset=UTF-8");
        /*获取字节输出流*/
        ServletOutputStream outputStream = response.getOutputStream();
        /*去调用业务逻辑层查数据*/
        List<Activity> activities = actyServoce.selectByIdarray(id);
        /*使用apache-poi插件生成excel文件  要先加依赖*/
        /*创建一个文件类  往里面加页*/
        HSSFWorkbook wb = new HSSFWorkbook();
        /*a往页里加行*/
        HSSFSheet sheet = wb.createSheet("市场活动");
        /*0代表第一行*/
        HSSFRow row = sheet.createRow(0);
        /*往行里加列*/
        HSSFCell cell = row.createCell(0);
        /*给0行0列设置值*/
        cell.setCellValue("id");
        cell=row.createCell(1);/*设置第二列*/
        cell.setCellValue("所有者");
        cell=row.createCell(2);
        cell.setCellValue("名称");
        cell=row.createCell(3);
        cell.setCellValue("开始时间");
        cell=row.createCell(4);
        cell.setCellValue("结束时间");
        cell=row.createCell(5);
        cell.setCellValue("成本");
        cell=row.createCell(6);
        cell.setCellValue("描述信息");
        cell=row.createCell(7);
        cell.setCellValue("创建时间");
        cell=row.createCell(8);
        cell.setCellValue("创建人");
        cell=row.createCell(9);
        cell.setCellValue("修改时间");
        cell=row.createCell(10);
        cell.setCellValue("修改人");
        /*表头设置好了 设置表的身体 遍历list集合*/
        for(int i=0;i<activities.size();i++){
            /*查出来几行就要设置几行*/
            row= sheet.createRow(i+1);
            cell = row.createCell(0);
            /*给0行0列设置值*/
            cell.setCellValue(activities.get(i).getId());
            cell=row.createCell(1);/*设置第二列*/
            cell.setCellValue(activities.get(i).getOwner());
            cell=row.createCell(2);
            cell.setCellValue(activities.get(i).getName());
            cell=row.createCell(3);
            cell.setCellValue(activities.get(i).getStartDate());
            cell=row.createCell(4);
            cell.setCellValue(activities.get(i).getEndDate());
            cell=row.createCell(5);
            cell.setCellValue(activities.get(i).getCost());
            cell=row.createCell(6);
            cell.setCellValue(activities.get(i).getDescription());
            cell=row.createCell(7);
            cell.setCellValue(activities.get(i).getCreateTime());
            cell=row.createCell(8);
            cell.setCellValue(activities.get(i).getCreateBy());
            cell=row.createCell(9);
            cell.setCellValue(activities.get(i).getEditTime());
            cell=row.createCell(10);
            cell.setCellValue(activities.get(i).getEditBy());
        }
        /*直接通过wb.write(浏览器输出流) 输出到浏览器
        * 在这之前要设置响应头 告诉浏览器不要直接解析显示到页面(浏览器默认) 而是弹出下载框*/
        response.addHeader("content-Disposition","attachment;filename=shichangchoose.xls");
        wb.write(outputStream);
        wb.close();
    }

查询的SQL

  <select id="selectByIdarray" resultMap="BaseResultMap" parameterType="string">
    select a.id,u1.name as owner,a.name,a.start_date,a.end_date,
           a.cost,a.description,a.create_time,u2.name as create_by,
           a.edit_time,u3.name as edit_by
    from tbl_activity as a
           join tbl_user as u1 on a.owner=u1.id
           join tbl_user as u2 on a.create_by=u2.id
           left join  tbl_user as u3 on a.edit_by=u3.id
    where a.id in <foreach collection="array" item="id" close=")" open="(" separator=",">
            #{id}
  </foreach>
    order by a.create_time desc
  </select>

8.文件导入

技术准备:

文件上传表单三要素:
表单的input的类型必须是file 考虑到controller层要接收这个文件要给input起一个name
必须使用post请求
form表单的enctype属性必须是multipart/form-data 这个是多样性的表单数据编码(默认的是urlencodeed这个只能对文本数据进行编码)

excel文件上传之后还是使用apache-poi插件解析

文件上传在controller层还是要使用springmvc提供的一个类来接收文件MultpartFile这个类
但必须在springmvc的配置文件中配置上传解析器

    <!-- 配置文件上传解析器 id:必须是multipartResolver-->
   <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="#{1024*1024*80}"/>
        <property name="defaultEncoding" value="utf-8"/>
    </bean>

加依赖apache-poi

 <dependency>
		      <groupId>org.apache.poi</groupId>
		      <artifactId>poi</artifactId>
		      <version>3.15</version>
</dependency>

在controller层


 /*接收ajax请求发来的文件 利用Apache-poi插件解析文件 封装到实体类中
    * 返回响应信息*/
    @RequestMapping("/workbench/activity/importAct.do")
    @ResponseBody               /*提交过来的文件会注入到这里 但先要在springmvc中配置文件上传解析器*/
    public Object importAct(MultipartFile file,HttpSession session){
        usercan usercan = new usercan();

        /*先把文件写到服务器  在从服务器通过Apache-poi插件解析文件*/
        /*获取文件名*/
       // String originalFilename = file.getOriginalFilename();
        /*这个方法是将文件通过文件流 写出去  如果写到服务器出错了直接提示用户了 所以要try*/
        try {
                                                                               file.transferTo(new File("E:\aa\" + originalFilename + ""));
                                                                                    /*通过Input流读取文件*/
                                       这就可以不要了                                  FileInputStream fi=new FileInputStream("E:\aa\" + originalFilename + "");
                                                                                      /*获取session方便取user 创建list 和实体类*/




   InputStream fi = file.getInputStream(); //这里直接通过文件获取输入流直接传给wb去解析


            User user= (User) session.getAttribute(chang.SESSION_NAME);
            List<Activity> list=new ArrayList<>();
            /*解析*/
            HSSFWorkbook wb = new HSSFWorkbook(fi);
            /*获取文件中的页*/
            HSSFSheet at = wb.getSheetAt(0);
            HSSFRow row=null;
            HSSFCell cell=null;
            /*获取页中的最后一行的下标方便遍历*/
            int lastRowNum = at.getLastRowNum();
            for(int i=1;i<lastRowNum+1;i++){
                row = at.getRow(i);
                /*每遍历一行就创建一个实体类对象存数据*/
                Activity activity = new Activity();
                /*id生成 所有者和创建人都是当前用户 创建时间就是当前时间*/
                activity.setId(UUIDsaner.getUUID());
                activity.setOwner(user.getId());
                activity.setCreateBy(user.getId());
                activity.setCreateTime(SimDate.getdate(new Date()));
                /*遍历列  这个是获取列的总列数*/
                short lastCellNum = row.getLastCellNum();
                for(int j=0;j<lastCellNum;j++){
                   cell = row.getCell(j);
                   /*获取列中的值 按顺序设置到实体类中*/
                    String getcellvalue = Cellvalue.getcellvalue(cell);
                    if(j==0){
                        activity.setName(getcellvalue);
                    }else if(j==1){
                        activity.setStartDate(getcellvalue);
                    }else if(j==2){
                        activity.setEndDate(getcellvalue);
                    }else if(j==3){
                        activity.setCost(getcellvalue);
                    }else if(j==4){
                        activity.setDescription(getcellvalue);
                    }
                }
                /*每循环一行就把实体类加在list中*/
                list.add(activity);
            }
            /*把封装好的list调用service层*/
            int i = actyServoce.insertActivityList(list);
            /*只要没报异常就说明插入成功 返回响应信息*/
            usercan.setCode(chang.CODE_SUCCESS);
            usercan.setData(i+"");

        }catch (Exception e){
            e.printStackTrace();
            usercan.setMessage("系统繁忙请等待一小会儿重试");
            usercan.setCode(chang.CODE_SHIBAI);
        }
        return usercan;
    }

Apache-poi插件在解析excel文件时的获取文件中的数据的数据格式方法

    public static String getcellvalue(HSSFCell cell){
        String value="";
        if(cell.getCellType()==HSSFCell.CELL_TYPE_STRING){
           value= cell.getStringCellValue();
        }else if(cell.getCellType()==HSSFCell.CELL_TYPE_BOOLEAN){
            value=cell.getBooleanCellValue()+"";
        }else if(cell.getCellType()==HSSFCell.CELL_TYPE_NUMERIC){
            value=cell.getNumericCellValue()+"";
        }else if(cell.getCellType()==HSSFCell.CELL_TYPE_FORMULA){
            value=cell.getCellFormula();
        }else {
            value="";
        }
        return value;
    }

底层的SQL语句

  <insert id="insertActivityList" parameterType="activity">
    insert into tbl_activity(id, owner, name, start_date,
                             end_date, cost, description,
                             create_time, create_by)
    values
    <foreach collection="list" item="obj" separator=",">
      (#{obj.id},#{obj.owner},#{obj.name},#{obj.startDate},#{obj.endDate}
      ,#{obj.cost},#{obj.description},#{obj.createTime},#{obj.createBy})
    </foreach>
  </insert>

前端用的ajax发请求和传文件参数

	/*给导入模态窗口中的导入加单击事件 收集参数 验证参数*/
		$("#importActivityBtn").click(function (){
			/*获取用户上传文件的参数名 验证是不是.xls结尾 和文件大小不能超过5mb  通过导入标签获取*/
			var filename=$("#activityFile").val();
			var f=filename.substr(filename.lastIndexOf(".")+1).toLocaleLowerCase() /*获取后缀转化为小写*/
			if(f!="xls"){
				alert("必须是以xls后缀结尾的文件")
				return
			}
			/*通过这个文件上传标签的dom对象的files属性可以获取文件数组 .size属型来判断文件大小*/
			var file=$("#activityFile").get(0).files[0];
			if(file.size>1024*1024*5){
				alert("文件超过了5MB")
				return;
			}
			/*利用FormData这个接口来传递参数  这个既可以串字符串数据  也可以串二进制数据 往里面加就行了*/
			var formdata=new FormData();
			formdata.append("file",file)
			/*发ajax请求 把文件传给后台 成功导入则关闭模态窗口刷新数据*/
			$.ajax({
				url:"workbench/activity/importAct.do",
				data:formdata,
				dataType:"json",
				type:"POST",
				processData:false,  //这个参数是是否在发请求之前把参数转化为字符串 默认true
				contentType:false,//这个参数是是否把参数转化为的字符串用urlenconded编码  因为是传二进制文件所以都为false
				success:function (data){
					if(data.code=="1"){
						alert("成功导入"+data.data+"条数据")
						queryhuodong(1,$("#fenye").bs_pagination('getOption','rowsPerPage'))
						$("#importActivityModal").modal("hide")
					}else {
						alert(data.message)
						$("#importActivityModal").modal("show")
					}
				}
			})

		})


 <input type="file" id="activityFile">

9.市场活动明细页面

给标签添加属性:
如果是表单组件标签,优先使用value属性,只有value不方便使用时,使用自定义属性;
如果不是表单组件标签,不推荐使用value,推荐使用自定义属性。

获取属性值时:
如果获取表单组件标签的value属性值:dom对象.value
jquery对象.val()
如果自定义的属性,不管是什么标签,只能用:jquery对象.attr(“属性名”);

mapper层

    /*根据id查询市场活动的信息 显示人名用连接查询*/
    Activity selectActivityById(String id);

  <select id="selectActivityById" parameterType="string" resultMap="BaseResultMap">
    select a.id,u1.name as owner,a.name,a.start_date,a.end_date,
           a.cost,a.description,a.create_time,u2.name as create_by,
           a.edit_time,u3.name as edit_by
    from tbl_activity as a
           join tbl_user as u1 on a.owner=u1.id
           join tbl_user as u2 on a.create_by=u2.id
           left join  tbl_user as u3 on a.edit_by=u3.id
    where a.id=#{id}
  </select>


    /*根据市场活动的id查全部的活动备注*/
       List<activityRemark> selectByActivityIdRemarks(String id);

  <select id="selectByActivityIdRemarks" resultMap="BaseResultMap" parameterType="string">
    select a.id,a.note_content,a.create_time,u1.name as create_by,
           a.edit_time,u2.name as edit_by,a.edit_flag
    from tbl_activity_remark as a
    join tbl_user as u1 on a.create_by=u1.id
    left join tbl_user as u2 on a.edit_by=u2.id
    where a.activity_id=#{id}
  </select>

controller层

    /*这个用来接收前端传过来的一个市场活动的id返回
    * 详情页面的所有信息 是存到请求域里面请求转发  通过jsp向浏览器返回网页*/
    @RequestMapping("/workbench/activity/selectActivityandbeizhu.do")
    public String selectActivityandbeizhu(String id, HttpServletRequest request){
        Activity activity = actyServoce.selectActivityById(id);
        List<activityRemark> activityRemarks = actRemarkSerivse.selectByActivityIdRemarks(id);
        request.setAttribute("activity",activity);
        request.setAttribute("activityRemarks",activityRemarks);

        return "workbench/activity/detail";
    }

页面片段:

<a style="text-decoration: none; cursor: pointer;" οnclick="window.location.href='workbench/activity/selectActivityandbeizhu.do?id="+actv.id+"'">


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
	<%String bg=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";%>
	<meta charset="UTF-8">
	<base href="<%=bg%>">

<link href="jquery/bootstrap_3.3.0/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
<script type="text/javascript" src="jquery/bootstrap_3.3.0/js/bootstrap.min.js"></script>


<%--备注信息用EL表达式  jstl标签库--%>
		<c:forEach items="${activityRemarks}" var="act">
			<div class="remarkDiv" style="height: 60px;">
				<img title="${act.createBy}" src="image/user-thumbnail.png" style="width: 30px; height:30px;">
				<div style="position: relative; top: -40px; left: 40px;" >
					<h5>${act.noteContent}</h5>																			<%--这里是刚创建就显示创建时间  被修改过就显示修改时间--%>
					<font color="gray">市场活动</font> <font color="gray">-</font> <b>${activity.name}</b> <small style="color: gray;"> ${act.editFlag=="1"?act.editTime:act.createTime } 由${act.editFlag=="1"?act.editBy:act.createBy }${act.editFlag=="1"?'修改':'创建' }</small>
					<div style="position: relative; left: 500px; top: -30px; height: 30px; width: 100px; display: none;">  <%--给这两个修改和删除图标加上自定义属性 这个备注的id 修改删除要有 自定义属性只能用Jquery.attr("属性名")获取--%>
						<a class="myHref" href="javascript:void(0);" beizhuid="${act.id}"><span class="glyphicon glyphicon-edit" style="font-size: 20px; color: #E6E6E6;"></span></a>
						&nbsp;&nbsp;&nbsp;&nbsp;
						<a class="myHref" href="javascript:void(0);" beizhuid="${act.id}"><span class="glyphicon glyphicon-remove" style="font-size: 20px; color: #E6E6E6;"></span></a>
					</div>
				</div>
			</div>
		</c:forEach>

10.市场活动详细页面的新增备注功能

用户只输入备注的内容,用户点击添加,添加成功之后清空输入框,刷新备注列表(在controller层要返回这个增加的备注实体类对象,因为前端要用$(“#remarkDiv”).before(html) 在这个标签外部的前面加上摄入的内容)

2,把页面片段显示在动态显示在页面中:
选择器.html(htmlStr):覆盖显示在标签的内部
选择器.text(htmlStr):覆盖显示在标签的内部
选择器.append(htmlStr):追加显示在指定标签的内部的后边
$(“#remarkDiv”).before(html) /在这个标签外部的前面加上摄入的内容/
选择器.after(htmlStr):追加显示在指定标签的外部的后边

mapper层

    /*用户点击保存市场活动备注 插入市场活动*/
    int insertAllactivityRemark(activityRemark activityRemark);


  <insert id="insertAllactivityRemark" parameterType="activityRemark">
    insert into tbl_activity_remark(id, note_content, create_time, create_by,edit_flag, activity_id)
    values(#{id},#{noteContent},#{createTime},#{createBy},#{editFlag},#{activityId})
  </insert>

controller层

  /*添加备注  接收前端传过来的两个参数 一个备注信息 和一个市场活动id
        返回响应信息 要把这里封装要插入数据库的数据返回到前端 因为前端直接动态追加显示 不再查数据库了*/
    @RequestMapping("/workbench/activity/insertbeizhu.do")
    @ResponseBody
    public Object insertbeizhu(activityRemark activityRemark,HttpSession session){
        /*封装参数 创建用户的id从session中获取*/
        User user= (User) session.getAttribute(chang.SESSION_NAME);
        activityRemark.setCreateBy(user.getId());
        activityRemark.setCreateTime(SimDate.getdate(new Date()));
        activityRemark.setId(UUIDsaner.getUUID());
        activityRemark.setEditFlag(chang.BEI_ZHU_NO_EIDT);

        usercan usercan = new usercan();
        /*调用业务逻辑层返回结果*/
        try {
            int i = actRemarkSerivse.insertAllactivityRemark(activityRemark);
            if(i>0){
                usercan.setCode(chang.CODE_SUCCESS);
                usercan.setData(activityRemark);
            }else {
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("没有添加备注成功");
            }
        }catch (Exception e){
            e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("没有添加备注成功");
        }

        return usercan;
    }

前端jsp页面


		/*给保存按钮家单击事件 点击保存 收集参数
		* 发ajax请求 刷新页面*/
		$("#baocun11").click(function (){
			/*收集参数*/
			var noteContent=$("#remark").val();
			var activityId='${activity.id}' /*这里获取在request作用域的数据可以获取 应为在同一个页面*/
			/*表单验证*/
			if(noteContent==""){
				alert("备注不能为空")
				return
			}
			$.ajax({
				url:"workbench/activity/insertbeizhu.do",
				data:{
					noteContent:noteContent,
					activityId:activityId
				},
				type:"POST",
				dataType:"json",
				success:function (data){
					/*这里做字符串的拼接操作  然后动态显示到页面*/
					if(data.code=="1"){
						var html='';
						html+="<div class="remarkDiv" style="height: 60px;">";
						html+="	<img title="${sessionScope.sessionUser.name}" src="image/user-thumbnail.png" style="width: 30px; height:30px;">";
						html+="	<div style="position: relative; top: -40px; left: 40px;" >";
						html+="<h5>"+data.data.noteContent+"</h5>";																			<%--这里是刚创建就显示创建时间  被修改过就显示修改时间--%>
						html+="<font color="gray">市场活动</font> <font color="gray">-</font> <b>${activity.name}</b> <small style="color: gray;"> "+data.data.createTime+"由${sessionScope.sessionUser.name}创建</small>";
						html+="<div style="position: relative; left: 500px; top: -30px; height: 30px; width: 100px; display: none;">";  <%--给这两个修改和删除图标加上自定义属性 这个备注的id 修改删除要有 自定义属性只能用Jquery.attr("属性名")获取--%>
						html+="<a class="myHref" href="javascript:void(0);" beizhuid=""+data.data.id+""><span class="glyphicon glyphicon-edit" style="font-size: 20px; color: #E6E6E6;"></span></a>";
						html+="&nbsp;&nbsp;&nbsp;&nbsp;";
						html+="<a class="myHref" href="javascript:void(0);" beizhuid=""+data.data.id+""><span class="glyphicon glyphicon-remove" style="font-size: 20px; color: #E6E6E6;"></span></a>";
						html+="</div>";
						html+="</div>";
						html+="</div>";
						$("#remarkDiv").before(html)  /*在这个标签外部的前面加上摄入的内容*/
						$("#remark").val("")  /*清空内容*/

					}else{
						alert(data.message)
					}
				}
			})
		})


/*		$(".remarkDiv").mouseover(function(){
			$(this).children("div").children("div").show();
		});*/
		/*用父子选择器on来给所有的备注列表加动态的标签的鼠标移入事件*/
		$("#beizhufu").on('mouseover',".remarkDiv",function (){
			$(this).children("div").children("div").show();
		})

11.删除市场活动备注功能

给元素扩展属性:html页面是可扩展的标记语言,可以给指定的标签任意扩展属性,只要属性名符合标识符的命名规则即可。
两个目的:
1)使用标签保存数据:
如果是表单组件标签,优先使用value属性,只有value不方便使用时,使用自定义属性;
如果不是表单组件标签,不推荐使用value,推荐使用自定义属性。
2)定位标签:
优先考虑id属性,其次考虑name属性,只有id和name属性都不方便使用时,才考虑使用自定义属性。
1.用户点击删除图标发送ajax请求 传参数的备注的id值
这里怎么获取删除图标的Jquery对象用来给所有删除图标加单击事件呢???
通过给删除图标所在的标签扩展属性 用name 修改图标也是
这里加单击事件要用父子选择器.on的方式 因为备注的div是动态的
2收集参数:
用Jquery.attr(“属性名获取”)
这里this代表的是发生这个事件的标签的dom对象 所以$(this).attr()就行了
3.在controller层收集参数调用serivse层去删除备注根据id
4.controller返回是否删除成功的响应信息 用json
5.页面如果收到删除成功 那么就删除这个备注所在的div
如何定位这个备注的div???
给每个div加上id属性 这个值就是用备注的id 这样来确保唯一性 还可以在id前加上前缀进一部保证唯一性
$(“div”).remove()

前端:

$("#beizhufu").on("click","a[name='shanchutubiao']",function (){
			/*收集参数 这里this代表的是发生这个事件的标签 所以$(this).attr()就行了*/
		var id=	$(this).attr("beizhuid");
		$.ajax({
			url: "workbench/activity/deletebeizhu.do",
			data:{id:id},
			dataType: "json",
			type:"POST",
			success:function (data){
				if(data.code=="1"){
					/*5.页面如果收到删除成功 那么就删除这个备注所在的div
	如何定位这个备注的div???
	给每个div加上id属性 这个值就是用备注的id 这样来确保唯一性 还可以在id前加上前缀进一部保证唯一性*/
					$("#div_"+id).remove()

				}else {
					alert(data.message)
				}
			}
		})
		})

后端:

    /*3.在controller层收集参数调用serivse层去删除备注根据id*/
    @RequestMapping("/workbench/activity/deletebeizhu.do")
    @ResponseBody
    public Object deletebeizhu(String id){
        usercan usercan = new usercan();
        try {
            int i = actRemarkSerivse.deleteByIdInt(id);
            if(i>0){
                usercan.setCode(chang.CODE_SUCCESS);
            }else {
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("系统忙请稍后。。。");
            }
        }catch (Exception e){
            e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("系统忙请稍后。。。");
        }
        return usercan;
    }

mapper层的sql语句

  <delete id="deleteByIdInt" parameterType="string">
    delete from tbl_activity_remark
    where id=#{id}
  </delete>

12.修改市场活动备注功能

$("#beizhufu").on('click',"a[name='xiugaitubiao']",function (){
var id=$(this).attr("beizhuid");
$("#div_"+data.data.id+"  h5").text(data.data.noteContent)

1.用户点击修改的图标,给所有图标加上单击事件 把备注id放在模态窗口的隐藏标签里方便点击更新的时候拿

/*1.用户点击修改的图标,给所有图标加上单击事件*/
		$("#beizhufu").on('click',"a[name='xiugaitubiao']",function (){
			/*拿到这个修改中放的id*/
			var id=$(this).attr("beizhuid");
			/*通过id定位到最外层的div 在通过父子选择器选择中h5*/
			var noteContent=$("#div_"+id+"  h5").text();
			/*把这些内容放在模态窗口上*/
			$("#beizhuidyincang").val(id)
			$("#noteContent").val(noteContent)
			/*打开模态窗口*/
			$("#editRemarkModal").modal("show")

		})

2.点击完弹出修改的模态窗口,用户填写完修改后的备注 点击更新

3.发送ajax请求参数为这个市场活动备注的id和内容

$("#updateRemarkBtn").click(function (){
			/*获取参数 表单验证*/
			var id=$("#beizhuidyincang").val();
			var noteContent=$("#noteContent").val()
			if(noteContent==""){
				alert("修改的备注不能为空")
				return;
			}
			/*发ajax请求*/
			$.ajax({
				url:"workbench/activity/uptateByidRemark.do",
				data:{
					id:id,
					noteContent:noteContent
				},
				dataType:"json",
				type:"POST",
				success:function (data){
					/*如果成功就动态刷新页面*/
					if(data.code=="1"){
						$("#div_"+data.data.id+"  h5").text(data.data.noteContent)
						$("#div_"+data.data.id+"   small").text(""+data.data.editTime+"由${sessionScope.sessionUser.name}修改")
						/*关闭模态窗口*/
						$("#editRemarkModal").modal("hide")
					}else {
						alert(data.message)
						$("#editRemarkModal").modal("show")
					}
				}
			})
		})

4.controller层拿到参数 后还要封装实体类的其他几个参数 修改时间 修改人(session里拿)还有修改标记改为1
5.调用业务逻辑层看修改成功还是失败 返回响应信息 要把实体类返回去 应为前端要动态修改这个div

 @RequestMapping("/workbench/activity/uptateByidRemark.do")
    @ResponseBody
    public Object uptateByidRemark(activityRemark activityRemark,HttpSession session){
        usercan usercan = new usercan();
        User user= (User) session.getAttribute(chang.SESSION_NAME);
        activityRemark.setEditFlag(chang.BEI_ZHU_YES_EIDT);
        activityRemark.setEditBy(user.getId());
        activityRemark.setEditTime(SimDate.getdate(new Date()));

        /*调用service层*/
        try {
            int i = actRemarkSerivse.updateActivityRemarkId(activityRemark);
            if(i>0){
                usercan.setCode(chang.CODE_SUCCESS);
                usercan.setData(activityRemark);
            }else {
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("系统忙请稍后哦");
            }
        }catch (Exception e){
            e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("系统忙请稍后哦");
        }
        return usercan;
    }

修改的sql

  <update id="updateActivityRemarkId" parameterType="string">
    update tbl_activity_remark set note_content=#{noteContent},edit_flag=#{editFlag},edit_time=#{editTime},edit_by=#{editBy}
    where id=#{id}
  </update>

6.前端拿到数据 通过之前的标签里面的备注id 定位到 最外层的div 通过这个最外层的div和父子选择器定位到里面的标签用来修改内容

十:线索模块的功能

1.线索页面的显示

1.用户点击线索 应为页面在web-inf下面所以要通过controller层的请求转发到达

<li class="liClass"><a href="workbench/clue/selectxialaliebiao.do" target="workareaFrame"><span class="glyphicon glyphicon-search"></span> 线索(潜在客户)</a></li>

2.这个线索页面的下拉列表里的数据是通过数据库里查出来的

  <select id="selectBytypecode" parameterType="string" resultMap="BaseResultMap">
    select <include refid="Base_Column_List"></include>
    from tbl_dic_value
    where type_code=#{typeCode}
  </select>

  <!--查所有用户-->
  <select id="queryAllUsers" resultMap="BaseResultMap">
    select <include refid="Base_Column_List"></include>
    from tbl_user
  </select>

3.所以在controller层要拿到下拉列报的数据 请求转发到页面

 /*这个controller的方法是给用户点击线索然后 把线索页面所要的数据通过request域转发到页面*/
    @RequestMapping("/workbench/clue/selectxialaliebiao.do")
    public String selectxialaliebiao(HttpServletRequest request){
        /*收集参数*/
        /*执行service层的方法拿到数据*/
        List<User> users = userServic.selectAllUsers();
        List<dicvalue> appellation = dicvlue.selectBytypecode("appellation");//称呼
        List<dicvalue> source = dicvlue.selectBytypecode("source");//来源
        List<dicvalue> stage = dicvlue.selectBytypecode("stage");//状态

        /*把数据封装到request域里面*/
        request.setAttribute("users",users);
        request.setAttribute("appellation",appellation);
        request.setAttribute("source",source);
        request.setAttribute("stage",stage);

        /*请求转发到页面*/
        return "workbench/clue/index";
    }

4.在页面用EL表达式和JSTL标签库来显示数据

<div class="form-group">
<label for="edit-source" class="col-sm-2 control-label">线索来源</label>
<div class="col-sm-10" style="width: 300px;">
<select class="form-control" id="edit-source">
	 <option></option>
	<c:forEach items="${source}" var="sou">
	<option value="${sou.id}">${sou.value}</option>
		</c:forEach>
</select>
</div>
</div>

 <%--下拉列表的value属性存id修改表的时候用--%>

2.创建线索的实现

1.用户点击模态窗口中的保存 ,收集参数发送ajax请求

/*给模态窗口中的保存加单击事件*/
		$("#baocun").click(function (){
			/*收集参数*/
			var fullname         =$("#create-fullname").val();
			var appellation      =$("#create-appellation").val();
			var owner            =$("#create-owner").val();
			var company          =$("#create-company").val();
			var job              =$("#create-job").val();
			var email            =$("#create-email").val();
			var phone            =$("#create-phone").val();
			var website          =$("#create-website").val();
			var mphone           =$("#create-mphone").val();
			var state            =$("#create-state").val();
			var source			=$("#create-source").val();
			var description      =$("#create-description").val();
			var contactSummary  =$("#create-contact_summary").val();
			var nextContactTime=$("#create-next_contact_time").val();
			var address          =$("#create-address").val();
			/*表单验证*/
			if(owner==""){
				alert("所有者不能为空")
				return
			}
			if(company==""){
				alert("公司不能为空")
				return
			}
			if(fullname==""){
				alert("姓名不能为空")
				return
			}
			/*验证手机号只能是0-9的数字用正则表达式*/
			var zhengze=/^[0-9]*$/
			if(!zhengze.test(mphone)){
				alert("手机号只能是0-9的数字组成")
				return;
			}

			/*发送ajax请求*/
			$.ajax({
				url:"workbench/clue/insertAll.do",
				data:{
					fullname:fullname,
					appellation:appellation,
					owner:owner,
					company:company,
					job:job,
					email:email,
					phone:phone,
					website:website,
					mphone:mphone,
					state:state,
					source:source,
					description:description,
					contactSummary:contactSummary,
					nextContactTime:nextContactTime,
					address:address
				},
				type:"POST",
				dataType:"json",
				success:function (data){
					if(data.code=="1"){
						/*关闭模态窗口刷新页面*/
						$("#createClueModal").modal("hide")
						/*刷新页面*/
/*刷新页面 显示第一页 每页显示条数不变*/
						selectxianxuo(1,$("#fenyechajian").bs_pagination('getOption','rowsPerPage'))
					}else {
						alert(data.message)
						$("#createClueModal").modal("show")
					}
				}
			})

		})

		/*给创建的下次联系时间加日历插件*/
		$("#create-next_contact_time").datetimepicker({
			language:"zh-CN",//设置语言为中文格式
			format:"yyyy-mm-dd",//设置选中完日历后返回给文本框的字符串格式
			minView:"month",//设置最小可以选择到哪里 写月最小显示到日
			autoclose:true,  //设置选完后关闭
			initialDate:new Date(), //设置初始化时候的选中 为当前时间
			todayBtn:true,  //设置在下面显示今天按钮 默认false不显示
			clearBtn:true,  //设置清空按钮 默认为false不显示  这里应为要显示中文的清空 所以改了源码bootstrap-theme.min.css这里面
			pickerPosition:'top-right'//在屏幕的上面显示日历
		})

2.controller层收集参数(实体类)然后继续封装id,创建时间,创建者

    @RequestMapping("/workbench/clue/insertAll.do")
    @ResponseBody
    public Object insertAll(Clue clue, HttpSession session){
        User user= (User) session.getAttribute(chang.SESSION_NAME);
        usercan usercan = new usercan();
        clue.setId(UUIDsaner.getUUID());
        clue.setCreateBy(user.getId());
        clue.setCreateTime(SimDate.getdate(new Date()));

        try{
            int i = clueService.insertByClue(clue);
            if(i>0){
                usercan.setCode(chang.CODE_SUCCESS);
            }else{
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("系统忙请稍后.....");
            }
        }catch (Exception e){
            e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("系统忙请稍后.....");
        }
        return usercan;
    }

3.调用servce层的新建方法返回影响记录条数

  <insert id="insertByClue" parameterType="clue">
    insert into tbl_clue(id, fullname, appellation, owner, company, job, email, phone, website, mphone, state,
                             source, create_by, create_time,description, contact_summary,
                             next_contact_time, address)
    values (#{id,jdbcType=CHAR}, #{fullname,jdbcType=VARCHAR}, #{appellation,jdbcType=VARCHAR},
            #{owner,jdbcType=CHAR}, #{company,jdbcType=VARCHAR}, #{job,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR},
            #{phone,jdbcType=VARCHAR}, #{website,jdbcType=VARCHAR}, #{mphone,jdbcType=VARCHAR},
            #{state,jdbcType=VARCHAR}, #{source,jdbcType=VARCHAR}, #{createBy,jdbcType=VARCHAR},
            #{createTime,jdbcType=CHAR},
            #{description,jdbcType=VARCHAR}, #{contactSummary,jdbcType=VARCHAR}, #{nextContactTime,jdbcType=CHAR},
            #{address,jdbcType=VARCHAR})
  </insert>
 

4.根据影响记录条数返回响应信息1成功0失败错误信息
5.前端ajax在回调函数里成功则刷新列表,关闭模态窗口。失败就显示错误信息,模态窗口不关

3.线索的分页查询函数实现

1.用户点击线索,就要在前端收集参数,和页号和每页显示条数,这个前端的查询定义成一个函数,发jaxa异步请求

2.controller层用一个一个参数收集数据,封装为map传给service层去查询总的线索和总记录条数

mapper层的SQL

  <select id="selectByClue" parameterType="map" resultMap="BaseResultMap">
    select c.id,c.fullname,dv3.value as appellation,c.company,c.phone,c.mphone,dv.value as source,
           u1.name as owner,dv2.value as state
    from tbl_clue as c
    left join tbl_dic_value as dv3 on c.appellation=dv3.id
    left join tbl_dic_value as dv on c.source=dv.id
    join tbl_user as u1 on c.owner=u1.id
    left join tbl_dic_value as dv2 on c.state=dv2.id
    <where>
      <if test="fullname!=null and fullname!=''">
        and c.fullname  like concat('%',#{fullname},'%')
      </if>
    <if test="company!=null and company!=''">
      and c.company  like concat('%',#{company},'%')
    </if>
    <if test="phone!=null and phone!=''">
      and c.phone like concat('%',#{phone},'%')
    </if>
      <if test="source!=null and source!=''">
        and dv.value =#{source}
      </if>
      <if test="owner!=null and owner!=''">
        and u1.name like concat('%',#{owner},'%')
      </if>
      <if test="mphone!=null and mphone!=''">
        and c.mphone like concat('%',#{mphone},'%')
      </if>
      <if test="state!=null and state!=''">
        and dv2.value =#{state}
      </if>
    </where>
    order by c.create_time desc
    limit #{pageNumber},#{pagesize}
  </select>

  <select id="selectAlltiao" parameterType="map" resultType="int">
    select count(*)
    from tbl_clue as c
    left join tbl_dic_value as dv3 on c.appellation=dv3.id
    left join tbl_dic_value as dv on c.source=dv.id
    left join tbl_user as u1 on c.owner=u1.id
    left join tbl_dic_value as dv2 on c.state=dv2.id
    <where>
      <if test="fullname!=null and fullname!=''">
        and c.fullname  like concat('%',#{fullname},'%')
      </if>
      <if test="company!=null and company!=''">
        and c.company  like concat('%',#{company},'%')
      </if>
      <if test="phone!=null and phone!=''">
        and c.phone like concat('%',#{phone},'%')
      </if>
      <if test="source!=null and source!=''">
        and dv.value =#{source}
      </if>
      <if test="owner!=null and owner!=''">
        and u1.name like concat('%',#{owner},'%')
      </if>
      <if test="mphone!=null and mphone!=''">
        and c.mphone like concat('%',#{mphone},'%')
      </if>
      <if test="state!=null and state!=''">
        and dv2.value =#{state}
      </if>
    </where>
  </select>

3.把数据封装为map转json返回给前端

 /*点击线索就返回线索的根据分页查询的所有记录和返回总记录条数*/
    @RequestMapping("/workbench/clue/selectfenye.do")
    @ResponseBody
    public Object selectfenye(String fullname,String company,String phone,String source,
                              String owner,String mphone,String state,int pageNumber,int pagesize){
        /*封装参数*/
        Map<String,Object> map=new HashMap<>();
        map.put("fullname",fullname);
        map.put("company",company);
        map.put("phone",phone);
        map.put("source",source);
        map.put("owner",owner);
        map.put("mphone",mphone);
        map.put("state",state);
        /*根据页号算出起始页数*/
        map.put("pageNumber",(pageNumber-1)*pagesize);
        map.put("pagesize",pagesize);
        /*调用业务逻辑层拿到参数*/
        int i = clueService.selectAlltiao(map);
        List<Clue> clues = clueService.selectByClue(map);
        /*封装响应数据为map*/
        Map<String,Object> map1=new HashMap<>();
        map1.put("Listclues",clues);
        map1.put("num",i);
        return map1;

    }

4.前端在ajax的回调函数里面拼接字符串显示列表 然后定义分页插件 需要根据总记录条数和每页显示条数算出总页数

/*定义一个查询的方法 查询线索列表 参数一:页号,参数二:每页显示条数*/
function selectxianxuo(pageNumber,pagesize){
	/*收集参数*/
	var fullname =$("#mingcheng").val()
	var company  =$("#gongsi").val()
	var phone   =$("#gongsizuoji").val()
	var source  =$("#xiansuolaiyuan").val()
	var owner   =$("#suoyouzhe").val()
	var mphone  =$("#shouji").val()
	var state    =$("#xiansuozhuangtai").val()
	var pageNumber=pageNumber
	var pagesize =pagesize

	/*发ajax请求*/
	$.ajax({
		url:"workbench/clue/selectfenye.do",
		data:{
			fullname:fullname,
			company:company,
			phone:phone,
			source:source,
			owner:owner,
			mphone:mphone,
			state:state,
			pageNumber:pageNumber,
			pagesize:pagesize
		},
		dataType:"json",
		type:"POST",
		success:function (data){
			/*拿到数据凭借列表*/
			var html='';
			$.each(data.Listclues,function (i,clue){
				    html+="<tr>";
					html+="<td><input type="checkbox" value='"+clue.id+"' /></td>";
					html+="<td><a style="text-decoration: none; cursor: pointer;" οnclick="window.location.href='detail.html';">"+clue.fullname+""+clue.appellation+"</a></td>";
					html+="<td>"+clue.company+"</td>";
					html+="<td>"+clue.phone+"</td>";
					html+="<td>"+clue.mphone+"</td>";
					html+="<td>"+clue.source+"</td>";
					html+="<td>"+clue.owner+"</td>";
					html+="<td>"+clue.state+"</td>";
				    html+="</tr>";
			})
			/*打入标签中*/
			$("#xiansuobody").html(html)
			/*计算出总页数*/
			var zongye=0
			if(data.num%pagesize==0){

				zongye=data.num/pagesize
			}
			else {
				/*js的系统函数 parseInt()将这个数取整数部分*/
				zongye=parseInt(data.num/pagesize)+1;
			}

			/*假如这个分页插件 一引入依赖 二建立容器 三往容器里加函数*/
			$("#fenyechajian").bs_pagination({
				currentPage:pageNumber,//当前页号 参数传
				totalRows:data.num,//总记录条数 ajax返回了
				totalPages:zongye, //总页数是这个函数的必填值 是根据总记录条数 每页显示条数算出来的
				rowsPerPage:pagesize,//每页显示条数
				visiblePageLinks: 5,//这个是那个卡片数最多5个
				showGoToPage: true,//显示跳转部分  默认true显示
				showRowsPerPage: true,//显示设置每页条数 默认true显示
				showRowsInfo: true,//显示记录的信息部分 默认true显示
				onChangePage:function (event,objpage) {
					/*这个是每次页号发生改变就会执行这个函数 第一个参数是这个事件源
					* 第二个参数是这个 页面对象 包含的是外面的函数发生页号改变的所有属性信息
					* 页号发生改变 上面的属性值也会跟着变*/
					//当页号发生改变是 再次调用这个函数 去打请求刷新这个页面 把改变后的当前页号和每页显示条数传给他
					selectxianxuo(objpage.currentPage,objpage.rowsPerPage);

				}
			})
		}
	})

5.在入口函数里调用这个函数

	selectxianxuo(1,5)

4.线索详细页面的显示

1.用户点击名称栏的超链接,发送同步请求,传递这个线索的id作为参数传给controller

html+="<td><a style="text-decoration: none; cursor: pointer;" οnclick="window.location.href='workbench/activity/selectActivityandbeizhu.do?id="+actv.id+"'">"+actv.name+"</a></td>"

2.controller层1.根据id查询这个线索的所有明细信息2.工具id查这个线索的所有备注信息3.根据id查这个线索参加过哪些市场活动

    @RequestMapping("/workbench/clue/selectxiangxi.do")
    public String selectxiangxi(String id,HttpServletRequest request){
        List<ClueRemark> clueRemarks = clueremarkService.selectByClueIdRemarks(id);
        List<Activity> Activitylist = actyServoce.selectByClueIdActivity(id);
        Clue clue = clueService.selectByIdone(id);
        request.setAttribute("clueRemarks",clueRemarks);
        request.setAttribute("Activitylist",Activitylist);
        request.setAttribute("clue",clue);

        return "workbench/clue/detail";

    }

3.查到这三个数据数据,封装到request域中,然后通过请求转发到详细页面
mapper层的SQL
线索 :

<select id="selectByIdone" resultMap="BaseResultMap" parameterType="string">
    select c.id,c.fullname,dv3.value as appellation, u1.name as owner,c.company,c.job,c.email,c.phone,c.website,c.mphone,dv.value as source,
          dv2.value as state,u2.name as create_by,c.create_time,u3.name as edit_by,
           c.edit_time,c.description,c.contact_summary,c.next_contact_time,c.address
    from tbl_clue as c
           left join tbl_dic_value as dv3 on c.appellation=dv3.id
           left join tbl_dic_value as dv on c.source=dv.id
           join tbl_user as u1 on c.owner=u1.id
           left join tbl_dic_value as dv2 on c.state=dv2.id
            join tbl_user as u2 on c.create_by=u2.id
            left join tbl_user as u3 on c.edit_by=u3.id
        where c.id=#{id}
  </select>

市场活动

 <select id="selectByClueIdActivity" resultMap="BaseResultMap" parameterType="string">
    select a.id,u1.name as owner,a.start_date,a.end_date
    from tbl_activity as a
    join tbl_user as u1 on a.owner=u1.id
    join tbl_clue_activity_relation as car on a.id=car.activity_id
    where car.clue_id=#{clueid}
  </select>

线索备注

  <select id="selectByClueIdRemarks" parameterType="string" resultMap="BaseResultMap">
    select a.id,a.note_content,a.create_time,u1.name as create_by,
           a.edit_time,u2.name as edit_by,a.edit_flag
    from tbl_clue_remark as a
           join tbl_user as u1 on a.create_by=u1.id
           left join tbl_user as u2 on a.edit_by=u2.id
    where a.clue_id=#{clueid}
    order by a.create_time asc
  </select>

4.在详细页面通过EL表达式和JSTL标签库显示数据(显示备注和市场活动时都要把id放入 方便增删)
关联的市场活动显示

					<c:forEach items="${Activitylist}" var="a">
						<tr id="tr_${a.id}">
							<td>${a.name}</td>
							<td>${a.startDate}</td>
							<td>${a.endDate}</td>
							<td>${a.owner}</td>
							<td><a href="javascript:void(0);"  style="text-decoration: none;" shichanghuodongid="${a.id}"><span class="glyphicon glyphicon-remove"></span>解除关联</a></td>
						</tr>
					</c:forEach>

备注显示

			<c:forEach items="${clueRemarks}" var="r">
				<div class="remarkDiv" style="height: 60px;" id="div_${r.id}">
					<img title="${r.createBy}" src="image/user-thumbnail.png" style="width: 30px; height:30px;">
					<div style="position: relative; top: -40px; left: 40px;" >
						<h5>${r.noteContent}</h5>
						<font color="gray">线索</font> <font color="gray">-</font> <b>${clue.fullname}${clue.appellation}-${clue.company}</b> <small style="color: gray;"> ${r.editFlag=="1"?r.editTime:r.createTime} 由${r.editFlag=="1"?r.editBy:r.createBy}${r.editFlag=="1"?'修改':'创建'}</small>
						<div style="position: relative; left: 500px; top: -30px; height: 30px; width: 100px; display: none;">
							<a class="myHref" href="javascript:void(0);" beizhuid="${r.id}"><span class="glyphicon glyphicon-edit" style="font-size: 20px; color: #E6E6E6;"></span></a>
							&nbsp;&nbsp;&nbsp;&nbsp;
							<a class="myHref" href="javascript:void(0);" beizhuid="${r.id}"><span class="glyphicon glyphicon-remove" style="font-size: 20px; color: #E6E6E6;"></span></a>
						</div>
					</div>
				</div>
			</c:forEach>

5.线索详细页面的查询市场活动

1.用户点击关联市场活动,弹出模态窗口,用户在搜索框输入关键字模糊查询出所有的市场活动,在列表中显示

		/*给关联市场活动按钮加单击事件打开模态窗口*/
		$("#guanlianshichanghuodong").click(function (){
			/*初始化工作*/

			$("#bundModal").modal("show")
		});

2.用户在搜索框搜索时 给搜索框加上一个键盘谈起事件(keyup),每弹起一次就发送一个ajax异步请求,参数是搜索框里的内容和这个线索的id(
因为要在数据库里查出来哪些市场活动没有被关联 只显示没有关联的市场活动,这要用到线索的id)

	/*给搜索框加键盘弹起事件*/
		$("#wenbenkuang").keyup(function (){
			/*收集参数  this代表当前发生这个事件的dom对象*/

			var text=this.value
			var clueId='${clue.id}'
			/*发ajax请求*/
			$.ajax({
				url:"workbench/clue/selectActivityByClueIdandText.do",
				data:{
					text:text,
					clueId:clueId
				},
				dataType:"json",
				type:"POST",
				success:function (data){
					/*遍历集合拼接字符串*/
					var html='';
					$.each(data,function (i,obj){
						html+="<tr>";
						html+="	<td><input type="checkbox" value='"+obj.id+"'/></td>";
						html+="	<td>"+obj.name+"</td>";
						html+="	<td>"+obj.startDate+"</td>";
						html+="	<td>"+obj.endDate+"</td>";
						html+="	<td>"+obj.owner+"</td>";
						html+="</tr>";
					})
					$("#shichanghuodongsuosou").html(html)
				}
			})
		});

3.在controller层收集参数,封装为map,调用service层的方法获取数据

    /*这是显示关联市场活动列表的controller
    3.在controller层收集参数,封装为map,调用service层的方法获取数据*/
    @RequestMapping("/workbench/clue/selectActivityByClueIdandText.do")
    @ResponseBody
    public Object selectActivityByClueIdandText(String text,String clueId){
        Map<String,Object> map=new HashMap<>();
        map.put("actname",text);
        map.put("clueid",clueId);
        List<Activity> actlist = actyServoce.selectByclueidandtext(map);
        return actlist;
    }

4.把数据返回至ajax回调函数
5.在前端,ajax收到参数,用字符串拼接列表,打入相应的元素中

mapper层的sql

  <select id="selectByclueidandtext" parameterType="map" resultMap="BaseResultMap">
    select a.id,a.name,u1.name as owner,a.start_date,a.end_date
    from tbl_activity as a
    join tbl_user as u1 on a.owner=u1.id
    where a.name like concat('%',#{actname},'%')  and a.id not in (
        select activity_id
        from tbl_clue_activity_relation
        where clue_id=#{clueid}
      )
  </select>

6.实现线索详细页面的市场活动的关联

1.用户在模态窗口选中要关联的市场活动,至少选择一个
收集参数,选中所有被选中的列表
遍历这个列表数组所有的市场活动的id和线索的id用字符串拼接成id=v1&id=v2…&clueid=v9的形式

/*给关联增加点击事件*/
		$("#guanlian").click(function (){
			/*收集参数*/
			var shuzu=$("#shichanghuodongsuosou input[type='checkbox']:checked");
			/*表单验证*/
			if(shuzu.size()==0){
				alert("请至少选择一个关联")
				return
			}
			var ids='';
			$.each(shuzu,function (){
				ids+="actId="+this.value+"&";
			})
			ids+="clueID=${clue.id}"

			$.ajax({
				url:"workbench/clue/guanlianshichanghuodong.do",
				data:ids,
				dataType: "json",
				type: "POST",
				success:function (data){
					/*8.前端在ajax的回调函数中,如果关联成功就拼接字符串(列表) 然后用.append()追加到tbody中*/
					var html='';
					if(data.code=="1"){
						$.each(data.data,function (i,obj){
							html+="<tr>";
							html+="<td>"+obj.name+"</td>";
							html+="<td>"+obj.startDate+"</td>";
							html+="<td>"+obj.endDate+"</td>";
							html+="<td>"+obj.owner+"</td>";
							html+="<td><a href="javascript:void(0);"  style="text-decoration: none;" shichanghuodongid=""+obj.id+""><span class="glyphicon glyphicon-remove"></span>解除关联</a></td>";
							html+="</tr>";
						})
						$("#xiangxiyeSCHD").append(html)
						$("#bundModal").modal("hide")

						/*自己思想:在ajax回调函数中 可以拿到所有被选中的列表,通过获取这个被选中的列表的父标签父到最外层的tr,
在ajax回调函数里面 只要插入成功就关闭模态窗口,直接遍历这个父标签tr数组删除列表*/
						var s=$("#shichanghuodongsuosou input[type='checkbox']:checked").parent().parent();
						$.each(s,function (){
							this.remove();
						})
					}else {
						alert(data.message)
						$("#bundModal").modal("show")
					}
				}
			})

		})

2.一点关联,发送ajax异步请求

3.controller层用String数组,和String变量接收市场活动id数组和线索id变量

4.把这些数据封装成市场活动线索关系表实体类型的list集合,每个市场活动id都对应这个线索的id
市场活动线索关系的id用UUID生成

    @RequestMapping("/workbench/clue/guanlianshichanghuodong.do")
    @ResponseBody
    public Object guanlianshichanghuodong(String[] actId,String clueID){
        /*遍历actid数组 和clueId组合成实体类封装到list中*/
        List<ClueActivityRelation> list=new ArrayList<>();
        usercan usercan = new usercan();
        ClueActivityRelation car=null;
        for(String i:actId){
            car=new ClueActivityRelation();
            car.setId(UUIDsaner.getUUID());
            car.setClueId(clueID);
            car.setActivityId(i);
            list.add(car);
        }
        try {
            int i = clueActivityrSerivce.insertAllList(list);
            if(i>0){
                /*插入成功 在去查那个市场活动*/
                List<Activity> list1 = actyServoce.selectByIdarray(actId);
                usercan.setCode(chang.CODE_SUCCESS);
                usercan.setData(list1);
            }else {
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("系统忙清稍后");
            }
        }catch (Exception e){
            e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("系统忙清稍后");
        }
        return usercan;
    }

5.调用service层的方法,在mapper层用的是foreach标签拼接来插入数据
市场活动:

 <select id="selectByArrayId" parameterType="string" resultMap="BaseResultMap">
    select a.id,a.name,u1.name as owner,a.start_date,a.end_date
    from tbl_activity as a
    join tbl_user as u1 on a.owner=u1.id
    where a.id in
    <foreach collection="array" item="actId" close=")" open="(" separator=",">
      #{actId}
    </foreach>
  </select>

关系表插入:

  <insert id="insertAllList" parameterType="clueActivityRelation">
    insert into tbl_clue_activity_relation(id,clue_id,activity_id)
    values
    <foreach collection="list"  item="obj" separator=",">
      (#{obj.id},#{obj.clueId},#{obj.activityId})
    </foreach>
  </insert>

6.如果插入成功,还要把刚刚选中的市场活动的信息查出来,应为要动态显示到页面
也是根据foreach拼接查

7.返回响应信息,成功或者失败 和查到的所有市场活动返回list,把这些参数封装为map返回
{code:1/0,
list:[{},{},{}],
message:“dsad”}

8.前端在ajax的回调函数中,如果关联成功就拼接字符串(列表) 然后用.append()追加到tbody中

9.用户再次点击关联市场活动 要清空text和列表
(或者,用户再次点击关联市场活动是不请空text,只把被选中添加的列表从列表中删除
自己思想:在ajax回调函数中 可以拿到所有被选中的列表,通过获取这个被选中的列表的父标签父到最外层的tr,
在ajax回调函数里面 只要插入成功就关闭模态窗口,直接遍历这个父标签tr数组删除列表

						var s=$("#shichanghuodongsuosou input[type='checkbox']:checked").parent().parent();
						$.each(s,function (){
							this.remove();
						})

7.实现线索详细页面的市场活动的删除

1.给每个解除关联增加点击事件,用户在页面点击解除关联市场活动,收集参数,收集这个市场活动的id和线索的id确定一条记录

$("#tr_"+activityId).remove()       $("#xiangxiyeSCHD").on("click","a",function (){       html+="<tr id="tr_"+obj.id+"">";  

2.发送ajax请求 ,controller接收到参数,用实体类接,调用service层的方法去表中删除记录

    @RequestMapping("/workbench/clue/deleteshichanghuodong.do")
    @ResponseBody
    public Object deleteshichanghuodong(ClueActivityRelation relation){
        usercan usercan = new usercan();
        try {
            int i = clueActivityrSerivce.deleteByActIdandClueId(relation);
            if(i>0){
                usercan.setCode(chang.CODE_SUCCESS);

            }else {
                usercan.setCode(chang.CODE_SHIBAI);
                usercan.setMessage("系统忙请稍后。。");
            }
        }catch (Exception e){
            e.printStackTrace();

            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("系统忙请稍后。。");
        }
        return usercan;
    }

mapper层的sql


  <delete id="deleteByActIdandClueId" parameterType="clueActivityRelation">
    delete from tbl_clue_activity_relation
    where clue_id=#{clueId} and activity_id=#{activityId}
  </delete>

3.controller返回响应信息,成功或者失败,

4.前端收到响应信息如果成功,就从页面删除这个市场活动 用市场活动的id扩展属性得到jquery对象删除

$("#xiangxiyeSCHD").on("click","a",function (){
			/*收集参数  扩展属性只能用jquery。attr获取 this代表当前正在发生事件的标签的dom对象*/
			var activityId=$(this).attr("shichanghuodongid")
			var clueId='${clue.id}'

			/*用户点击确认才发ajax请求去删除*/
			if(window.confirm("确定要删除吗")){
				/*发ajax请求*/
				$.ajax({
					url:"workbench/clue/deleteshichanghuodong.do",
					data:{
						activityId:activityId,
						clueId:clueId
					},
					dataType:"json",
					type:"POST",
					success:function (data){
						if(data.code=="1"){
							/*获取的当前的tr删除 tr用扩展属性id定位*/
							$("#tr_"+activityId).remove()
						}else {
							alert(data.message)
						}
					}
				})
			}
		})

8.线索详细页面点击转化按钮到转化页面

1.给转化增加单击事件,点击转化,发同步请求,参数是这个线索的id

		$("#zhuanhuan").click(function (){
			/*收集参数发同步请求*/
			window.location.href="workbench/clue/selectzhuanhua.do?id=${clue.id}"
		})

2.在controller层收集参数,调用service层,查到线索的明细,还有在交易的模态窗口里要显示下拉列表
所以还要在数据字典值表中去查询类型为stage的数据

    @RequestMapping("/workbench/clue/selectzhuanhua.do")
    public String selectzhuanhua(String id,HttpServletRequest request){
        Clue clue = clueService.selectByIdone(id);
        List<dicvalue> stage = dicvlue.selectBytypecode("stage");
        request.setAttribute("clue",clue);
        request.setAttribute("stage",stage);

        return "workbench/clue/convert";
    }

3.用request域封装参数,请求转发到转化页面

4.在转化页面中显示数据用EL表达式和JSTL标签库

9.转化页面的交易里面的市场活动源搜索关联过得市场活动

1.用户点击市场活动源,弹出模态窗口,用户在搜索框里输入name

$("#shichanghuodongyuan").click(function (){
			/*初始化工作*/
		$("#searchActivityModal").modal("show")
		})

2.给搜索框加上键盘弹起事件,参数为这个内容和这个线索的id(从域中取数据),发ajax请求

$("#sousuokuang").keyup(function (){
			/*收集参数*/
			var actname=this.value;
			var clueId='${clue.id}';
			/*发ajax请求*/
			$.ajax({
				url:"workbench/clue/selectByActivityAndClueIdguan.do",
				data:{
					actname:actname,
					clueId:clueId
				},
				dataType:"json",
				type:"POST",
				success:function (data){
					/*4.前端拿到数据,在ajax的回调函数里,动态拼接字符串,显示列表,要在单选框里扩展两个
属性,一个存市场活动id,一个存市场活动名字,应为在交易模态窗口要用*/
					var html='';
					$.each(data,function (i,obj){
						html+="<tr>";
						html+="	<td><input value='"+obj.id+"' actname='"+obj.name+"' type="radio" name="activity"/></td>";
						html+="	<td>"+obj.name+"</td>";
						html+="	<td>"+obj.startDate+"</td>";
						html+="	<td>"+obj.endDate+"</td>";
						html+="	<td>"+obj.owner+"</td>";
						html+="</tr>";
					})
					$("#jiaoyisoushichanghuodong").html(html)
				}
			})
		})

3.controller层拿到数据,封装为map,去查询和这个线索关联过的市场活动,返回市场活动list集合

    @RequestMapping("/workbench/clue/selectByActivityAndClueIdguan.do")
    @ResponseBody
    public Object selectByActivityAndClueIdguan(String actname,String clueId){
        /*封装参数*/
        Map<String,Object> map=new HashMap<>();
        map.put("actname",actname);
        map.put("clueId",clueId);
        /*调用service层方法*/
        List<Activity> list = actyServoce.selectAllByActivityNameAndClueIdguan(map);
        return list;
    }

4.前端拿到数据,在ajax的回调函数里,动态拼接字符串,显示列表,要在单选框里扩展两个
属性,一个存市场活动id,一个存市场活动名字,应为在交易模态窗口要用

html+="	<td><input value='"+obj.id+"' actname='"+obj.name+"' type="radio" name="activity"/></td>";

5.给每个单选框加上单击事件.on()加,用户选中就把这个单选框里的两个数据,市场活动id’放在隐藏域里面,
名字方法框里面,在关闭模态窗口

(获取自定义属性值要用JQuery.attr()获取 this this$("#jiaoyisoushichanghuodong").on('click',"input",function (){
			/*获取参数*/
			var actID=this.value
			var actname=$(this).attr("actname")
			/*打入id隐藏框 name打入显示框*/
			$("#yincangactid").val(actID)
			$("#activityname").val(actname)
			/*关闭模态窗口*/
			$("#searchActivityModal").modal("hide")
		})

mapper层用到的sql

  <select id="selectAllByActivityNameAndClueIdguan" parameterType="map" resultMap="BaseResultMap">
    select a.id,a.name,u1.name as owner,a.start_date,a.end_date
    from tbl_activity as a
    join tbl_user as u1 on a.owner=u1.id
    where a.name like concat('%',#{actname},'%') and a.id in(
        select activity_id
        from tbl_clue_activity_relation
        where clue_id=#{clueId}
      )
  </select>

10.线索详细页面转化按钮的实现

谁创建的所有者是谁
1.给转换按钮加单击事件,收集参数,(线索id,交易表单中的数据,是否选中了创建交易(checked属性))
2.表单验证,发ajax请求,在ajax回调函数里面成功就跳转到线索主页面,否则提示信息

$("#zhuanhuanxiansuo").click(function (){
			/*收集参数*/
			var clueID='${clue.id}';
			var money          =   $("#amountOfMoney").val()
			var name          =   $("#tradeName").val()
			var expectedDate   =   $("#expectedClosingDate").val()
			var stage              =   $("#stage").val()
			var activityId      =   $("#yincangactid").val()
			var xiaoyixuanzhongNO  =   $("#isCreateTransaction").prop("checked")
			/*表单验证 交易额不能小于0*/
			if(xiaoyixuanzhongNO=="true") {
				var zhengze = /^(([1-9]d*)|0)$/
				if (!zhengze.test(money)) {
					alert("交易额只能是非负整数")
					return
				}
			}
			$.ajax({
				url:"workbench/clue/zhuanhuanxiansuo.do",
				data:{
					clueId:clueID,
					money:money,
					name:name,
					expectedDate:expectedDate,
					stage:stage,
					activityId:activityId,
					xiaoyixuanzhongNO:xiaoyixuanzhongNO
				},
				dataType: "json",
				type: "POST",
				success:function (data){
					if(data.code=="1"){
						window.location.href="workbench/clue/selectxialaliebiao.do"
					}else {
						alert(data.message)
					}
				}
			})

		})

3.在controller层接收参数,封装为map对象,调用线索的service层的一个线索转化方法来完成全部的转换工作(因为要在一个事务中完成)

 @RequestMapping("/workbench/clue/zhuanhuanxiansuo.do")
    @ResponseBody
    public Object zhuanhuanxiansuo(String clueId,String money,String name,
                                   String expectedDate,String stage,String activityId,
                                   String xiaoyixuanzhongNO,HttpSession session){
        usercan usercan = new usercan();
        User user= (User) session.getAttribute(chang.SESSION_NAME);
        /*封装参数*/
        Map<String,Object> map=new HashMap<>();
        map.put("clueId",clueId);
        map.put("money",money);
        map.put("name",name);
        map.put("expectedDate",expectedDate);
        map.put("stage",stage);
        map.put("activityId",activityId);
        map.put("xiaoyixuanzhongNO",xiaoyixuanzhongNO);
        map.put(chang.SESSION_NAME,user);

/*调用service层*/
        try{
     clueService.xinsuozhuanhau(map);
        usercan.setCode(chang.CODE_SUCCESS);
    }catch (Exception e){
            e.printStackTrace();
            usercan.setCode(chang.CODE_SHIBAI);
            usercan.setMessage("系统忙请稍后。。。。");
        }
        return usercan;
    }

4.在service层,
4.1:先根据线索id,查询线索的全部信息(这里的id查的就是保存的id,因为到时候是插入表中的数据存的都是id),然后把查到的clue对象的
公司信息封装为客户表的实体类对象,在调用客户的mapper层插入数据

public void xinsuozhuanhau(Map<String, Object> map) {
        /*4.1:先根据线索id,查询线索的全部信息(这里的id查的就是保存的id,因为到时候是插入表中的数据存的都是id),然后把查到的clue对象的
公司信息封装为客户表的实体类对象,在调用客户的mapper层插入数据*/
        String clueID = (String) map.get("clueId");
        String shifou = (String) map.get("xiaoyixuanzhongNO");
        User user= (User) map.get(chang.SESSION_NAME);
        Clue clue = clueMapper.SelectByIdNo(clueID);
        Customer customer = new Customer();
        /*谁创建的所有者是谁 这个当前用户对象有controller层通过map传过来*/
        customer.setCreateBy(user.getId());
        customer.setCreateTime(SimDate.getdate(new Date()));
        customer.setId(UUIDsaner.getUUID());
        customer.setAddress(clue.getAddress());
        customer.setDescription(clue.getDescription());
        customer.setContactSummary(clue.getContactSummary());
        customer.setName(clue.getCompany());
        customer.setNextContactTime(clue.getNextContactTime());
        customer.setOwner(user.getId());
        customer.setPhone(clue.getPhone());
        customer.setWebsite(clue.getWebsite());
      customerMapper.insertByCustomer(customer);

用到的sql

  <select id="SelectByIdNo" resultMap="BaseResultMap" parameterType="string">
    select <include refid="Base_Column_List"></include>
    from tbl_clue
    where id=#{clueId}
  </select>

  <insert id="insertByCustomer" parameterType="customer">
    insert into tbl_customer(id,owner,name,website,phone,create_by,create_time,contact_summary,
                                 next_contact_time, description, address)
                values (#{id},#{owner},#{name},#{website},#{phone},#{createBy},#{createTime},#{contactSummary},#{nextContactTime},#{description},#{address})
  </insert>

4.2:然后把查到的clue对象的个人信息封装为联系人表的实体类对象,在调用联系人的mapper层插入数据

     Contacts contacts = new Contacts();
        contacts.setAddress(clue.getAddress());
        contacts.setCreateBy(user.getId());
        contacts.setCreateTime(SimDate.getdate(new Date()));
        contacts.setContactSummary(clue.getContactSummary());
        contacts.setId(UUIDsaner.getUUID());
        contacts.setDescription(clue.getDescription());
        contacts.setAppellation(clue.getAppellation());
        contacts.setCustomerId(customer.getId());
        contacts.setEmail(clue.getEmail());
        contacts.setFullname(clue.getFullname());
        contacts.setJob(clue.getJob());
        contacts.setMphone(clue.getMphone());
        contacts.setNextContactTime(clue.getNextContactTime());
        contacts.setOwner(user.getId());
        contacts.setSource(clue.getSource());
        contactsMapper.insertByContacts(contacts);

用到的sql

  <insert id="insertByContacts" parameterType="contacts">
    insert into  tbl_contacts(id, owner,source, customer_id, fullname, appellation, email, mphone, job, create_by,
      create_time,description, contact_summary, next_contact_time,
      address)
    values (#{id},#{owner},#{source},#{customerId},#{fullname},#{appellation},#{email},#{mphone},#{ job},#{ createBy},#{createTime},#{description},#{ contactSummary},#{ nextContactTime},#{address});
  </insert>

4.3:再根据线索id查询这个线索的所有备注,分别忘客户备注表和联系人备注表中加一份

        List<ClueRemark> clueRemarks = clueRemarkMapper.selectByClueIdbeizhu(clueID);
        ContactsRemark contactsRemark=null;
        CustomerRemark customerRemark=null;
        List<ContactsRemark> ctrList=new ArrayList<>();
        List<CustomerRemark> cmrList=new ArrayList<>();
        for(ClueRemark i:clueRemarks){
         contactsRemark = new ContactsRemark();
         customerRemark = new CustomerRemark();
         contactsRemark.setContactsId(contacts.getId());
         contactsRemark.setCreateBy(i.getCreateBy());
         contactsRemark.setCreateTime(i.getCreateTime());
         contactsRemark.setId(UUIDsaner.getUUID());
         contactsRemark.setEditBy(i.getEditBy());
         contactsRemark.setEditTime(i.getEditTime());
         contactsRemark.setEditFlag(i.getEditFlag());
         contactsRemark.setNoteContent(i.getNoteContent());
        ctrList.add(contactsRemark);

         customerRemark.setCustomerId(customer.getId());
         customerRemark.setCreateBy(i.getCreateBy());
         customerRemark.setCreateTime(i.getCreateTime());
         customerRemark.setId(UUIDsaner.getUUID());
         customerRemark.setEditBy(i.getEditBy());
         customerRemark.setEditTime(i.getEditTime());
         customerRemark.setEditFlag(i.getEditFlag());
         customerRemark.setNoteContent(i.getNoteContent());
        cmrList.add(customerRemark);
        }
        if(ctrList!=null && ctrList.size()>0) {
            contactsRemarkMapper.insertByContactsRemark(ctrList);
        }
        if(cmrList!=null && cmrList.size()>0) {
            customerRemarkMapper.insertByCustomerRemark(cmrList);
        }

用到的sql


  <select id="selectByClueIdbeizhu" parameterType="string" resultMap="BaseResultMap">
    select <include refid="Base_Column_List"></include>
    from tbl_clue_remark
    where clue_id=#{clueId}
  </select>

  <insert id="insertByContactsRemark" parameterType="contactsRemark">
    insert into tbl_contacts_remark(id, note_content, create_by, create_time, edit_by, edit_time, edit_flag, contacts_id)
    values
    <foreach collection="list" item="obj" separator=",">
      (#{obj.id},#{obj.noteContent},#{obj.createBy},#{obj.createTime},#{obj.editBy},#{obj.editTime},#{obj.editFlag},#{obj.contactsId})
    </foreach>
  </insert>

 <insert id="insertByCustomerRemark" parameterType="customerRemark">
    insert into tbl_customer_remark(id, note_content, create_by, create_time, edit_by, edit_time, edit_flag, customer_id)
    values
    <foreach collection="list" item="obj" separator=",">
      (#{obj.id},#{obj.noteContent},#{obj.createBy},#{obj.createTime},#{obj.editBy},#{obj.editTime},#{obj.editFlag},#{obj.customerId})
    </foreach>
  </insert>

4.4:再根据线索id获取线索和市场活动的关联关系,转为联系人与市场活动的关联关系

        List<ClueActivityRelation> list = clueActivityRelationMapper.selectByClueIda(clueID);

        List<ContactsActivityRelation> carlist=new ArrayList<>();
        ContactsActivityRelation contactsActivityRelation = null;
        for(ClueActivityRelation i :list){
            contactsActivityRelation= new ContactsActivityRelation();
            contactsActivityRelation.setId(UUIDsaner.getUUID());
            contactsActivityRelation.setActivityId(i.getActivityId());
            contactsActivityRelation.setContactsId(contacts.getId());
            carlist.add(contactsActivityRelation);
        }
        contactsActivityRelationMapper.insetByContactsActivityRelation(carlist);

用到的sql

  <select id="selectByClueIda" parameterType="string" resultMap="BaseResultMap">
    select <include refid="Base_Column_List"></include>
from tbl_clue_activity_relation
where clue_id=#{clueId}
  </select>

  <insert id="insetByContactsActivityRelation" parameterType="contactsActivityRelation">
    insert into tbl_contacts_activity_relation(id, contacts_id, activity_id)
    values
    <foreach collection="list" item="obj" separator=",">
      (#{obj.id},#{obj.contactsId},#{obj.activityId})
    </foreach>
  </insert>

4.5:如果用户点击了创建交易框,那就要在service层用交易的实体类对象,创建交易数据

        if("true".equals(shifou)){
            Tran tran = new Tran();
            /*        map.put("money",money);
        map.put("name",name);
        map.put("expectedDate",expectedDate);
        map.put("stage",stage);
        map.put("activityId",activityId);*/
            tran.setMoney((String) map.get("money"));
            tran.setName((String) map.get("name"));
            tran.setStage((String) map.get("stage"));
            tran.setActivityId((String) map.get("activityId"));
            tran.setCreateBy(user.getId());
            tran.setCreateTime(SimDate.getdate(new Date()));
            tran.setId(UUIDsaner.getUUID());
            tran.setOwner(user.getId());
            tran.setContactsId(contacts.getId());
            tran.setExpectedDate((String) map.get("expectedDate"));
            tranMapper.insertByTran(tran);

用到的sql

  <insert id="insertByTran" parameterType="tran">
    insert into tbl_tran(    id, owner, money, name, expected_date, customer_id, stage, type, source, activity_id,
                             contacts_id, create_by, create_time, edit_by, edit_time, description, contact_summary,
                             next_contact_time)
    values (#{id},#{ owner},#{ money},#{ name},#{ expectedDate},#{ customerId},#{ stage},#{ type},#{ source},#{ activityId},#{ contactsId},#{ createBy},#{ createTime},#{ editBy},#{ editTime},#{ description},#{ contactSummary},#{ nextContactTime})
  </insert>

4.6:把备注信息再往交易表备注里存一份

            List<TranRemark> TranremarkList=new ArrayList<>();
            TranRemark tranRemark =null;
            for(ClueRemark i:clueRemarks){
               tranRemark= new TranRemark();
                tranRemark.setTranId(tran.getId());
                tranRemark.setCreateBy(i.getCreateBy());
                tranRemark.setCreateTime(i.getCreateTime());
                tranRemark.setId(UUIDsaner.getUUID());
                tranRemark.setEditBy(i.getEditBy());
                tranRemark.setEditTime(i.getEditTime());
                tranRemark.setEditFlag(i.getEditFlag());
                tranRemark.setNoteContent(i.getNoteContent());
                TranremarkList.add(tranRemark);
            }
            if(TranremarkList!=null && TranremarkList.size()>0) {
                tranRemarkMapper.insertByTranRemarks(TranremarkList);
            }
        }

用到的sql

  <insert id="insertByTranRemarks" parameterType="tranRemark">
    insert into tbl_tran_remark(    id, note_content, create_by, create_time, edit_by, edit_time, edit_flag, tran_id)
    values
    <foreach collection="list" item="obj" separator=",">
      (#{obj.id},#{obj.noteContent},#{obj.createBy},#{obj.createTime},#{obj.editBy},#{obj.editTime},#{obj.editFlag},#{obj.tranId})
    </foreach>
  </insert>

4.7:先删除线索的备注,再删除线索和市场活动的关联关系,再删除这个线索

        clueRemarkMapper.deleteBYIda(clueID);
        clueActivityRelationMapper.deleteByIda(clueID);
        clueMapper.deleteByIda(clueID);

  <delete id="deleteBYIda" parameterType="string">
    delete from tbl_clue_remark
    where clue_id=#{clueID}
  </delete>

  <delete id="deleteByIda" parameterType="string">
    delete from tbl_clue_activity_relation
    where clue_id=#{clueId}
  </delete>

  <delete id="deleteByIda" parameterType="string">
    delete from tbl_clue
    where id=#{clueID}
  </delete>

5.controller层返回响应信息成功或者失败就行

十一:交易功能的实现

1.用户点击菜单的交易来到交易页面

html转jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
	<%String bg=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";%>
	<meta charset="UTF-8">
	<base href="<%=bg%>">

1.用户点击交易菜单,发送同步请求,不用提交参数。

<li class="liClass"><a href="workbench/transaction/doindex.do" target="workareaFrame"><span class="glyphicon glyphicon-usd"></span> 交易(商机)</a></li>

2.在controller层,要查出来交易主页面要用到的动态数据,下拉列表里的东西(调用service查以前写过的方法)

    @RequestMapping("/workbench/transaction/doindex.do")
    public String doindex(HttpServletRequest request){
        List<dicvalue> stage = dicvalueMapper.selectBytypecode("stage");
        List<dicvalue> transactionType = dicvalueMapper.selectBytypecode("transactionType");
        List<dicvalue> source = dicvalueMapper.selectBytypecode("source");
        request.setAttribute("stage",stage);
        request.setAttribute("transactionType",transactionType);
        request.setAttribute("source",source);

        return "workbench/transaction/index";
    }

3.查出数据,封装在request域中,通过请求转发到主页面

4.在主页面用EL表达式和JSTL标签库显示

2.交易用户点击创建按钮跳转到创建页面

1.用户点击创建按钮,发同步请求,不用参数,准备跳转到创建页面

		$("#chuangjian").click(function (){
			window.location.href="workbench/transaction/tosave.do";
		})

2.controller层接收到请求,要先查出来创建页面要用到的数据,下拉列表里的值和用户表里的

    @RequestMapping("/workbench/transaction/tosave.do")
    public String tosave(HttpServletRequest request){
        List<User> users = userMapper.queryAllUsers();

        List<dicvalue> stage = dicvalueMapper.selectBytypecode("stage");
        List<dicvalue> transactionType = dicvalueMapper.selectBytypecode("transactionType");
        List<dicvalue> source = dicvalueMapper.selectBytypecode("source");
        request.setAttribute("stage",stage);
        request.setAttribute("transactionType",transactionType);
        request.setAttribute("source",source);
        request.setAttribute("users",users);

        return "workbench/transaction/save";
    }

3.查出数据,封装在request域中,通过请求转发到创建页面

4.在主页面用EL表达式和JSTL标签库显示

3.在创建页面点击市场活动源

1.用户点击市场活动源,弹出查询市场活动的模态窗口,

$("#shichanghuodongyuan").click(function (){

			$("#findMarketActivity").modal("show");
		})

2.给搜索框加上键盘抬起事件,发ajax请求,参数就是这个文本框内容,去模糊查询市场活动
3.在controller层收到参数,去模糊查询市场活动,返回市场活动的list集合

    @RequestMapping("/workbench/transaction/selectshichanghuodong.do")
    @ResponseBody
    public Object selectshichanghuodong(String text){
        List<Activity> list = activityMapper.selectAllByActivityaa(text);
        return list;
    }

4.在ajax的回调函数里拼接字符串显示列表

	/*2.给搜索框加上键盘抬起事件,发ajax请求,参数就是这个文本框内容,去模糊查询市场活动
*/		$("#sousuo1").keyup(function (){
	    /* 收集参数*/
		var text=	$("#sousuo1").val()
			/*发ajax*/
			$.ajax({
				url:"workbench/transaction/selectshichanghuodong.do",
				data:{text:text},
				dataType:"json",
				type:"POST",
				success:function (data){
					var html='';
					$.each(data,function (i,obj){
						html+="<tr>";
						html+="	<td><input value='"+obj.id+"' actname='"+obj.name+"' type="radio" name="activity"/></td>";
						html+="	<td>"+obj.name+"</td>";
						html+="	<td>"+obj.startDate+"</td>";
						html+="	<td>"+obj.endDate+"</td>";
						html+="	<td>"+obj.owner+"</td>";
						html+="</tr>";
					})
					$("#shichanghuodongaaa").html(html)

				}

			})
		})

5.给每一个市场活动的单选框加上单击事件

6.一点就把这个市场活动的名字显示在文本框里和市场活动的id保存在隐藏域里

		$("#shichanghuodongaaa").on('click',"input[type='radio']",function (){
			/*6.一点就把这个市场活动的名字显示在文本框里和市场活动的id保存在隐藏域里*/
			var actid=this.value
			var actname=$(this).attr("actname")
			$("#create-activitySrc").val(actname)
			$("#yincangactid").val(actid)
			/*关闭模态窗口*/
			$("#findMarketActivity").modal("hide")
		})

4.在创建页面点击联系人

1.用户点击搜索标签,然后打开模态窗口

		$("#lianxiren").click(function (){
			$("#findContacts").modal("show")
		})

2.在文本框上加键盘弹起事件 ,发ajax异步请求,参数为文本框内容,每次都去联系人表中模糊查询这个文本框名称的数据

$("#lianxirenname").keyup(function (){
			/*收集参数*/
			var text=$("#lianxirenname").val()

			$.ajax({
				url:"workbench/transaction/selectlianxiren.do",
				data:{text:text},
				dataType: "json",
				type: "POST",
				success:function (data){
					var html='';
					$.each(data,function (i,obj){
						html+="<tr>";
						html+="	<td><input type="radio" value='"+obj.id+"' ctsname='"+obj.fullname+"' name="activity"/></td>";
						html+="	<td>"+obj.fullname+"</td>";
						html+="	<td>"+obj.email+"</td>";
						html+="	<td>"+obj.mphone+"</td>";
						html+="</tr>";
					})
					$("#lianxirentbdy").html(html)
				}
			})
		})

3.controller层收到参数,调用service层方法去查询
用到的sql

  <select id="selectByCtsName" resultMap="BaseResultMap" parameterType="string">
    select id,fullname,email,mphone
    from tbl_contacts
    where fullname like concat('%',#{ctsName},'%')
  </select>
    @RequestMapping("/workbench/transaction/selectlianxiren.do")
    @ResponseBody
    public Object selectlianxiren(String text){
        List<Contacts> contacts = contactsService.selectByCtsName(text);
        return contacts;
    }

4.返回查到的联系人list

5.前端在ajax的回调函数里拼接字符串,显示在列表中
6.给每一个联系人的单选框加上单击事件

		$("#lianxirentbdy").on('click',"input[type='radio']",function (){
			/*6.一点就把这个市场活动的名字显示在文本框里和市场活动的id保存在隐藏域里*/
			var ctsid=this.value
			var ctsname=$(this).attr("ctsname")
			$("#create-contactsName").val(ctsname)
			$("#lianxirenyincang").val(ctsid)
			/*关闭模态窗口*/
			$("#findContacts").modal("hide")
		})

6.一点就把这个联系人的名字显示在文本框里和联系人的id保存在隐藏域里

5.用户点击阶段直接填入可能性配置

1.用户提供每个阶段对应的可能性大小的配置文件,这里我们用属性配置文件
资质审查=10
需求分析=20
价值建议=40
确定决策者=60
提案/报价=70
谈判/复审=90
成交=100
丢失的线索=0
因竞争丢失关闭=0
2.用户每次选择阶段中的数据 (改变事件),都发送ajax请求,参数为这个阶段的名称

		$("#create-transactionStage").change(function (){
			var jieduan= $("#create-transactionStage option:selected").text();

			if(jieduan==''){
				$("#create-possibility").val("")
				return
			}

			$.ajax({
				url:"workbench/transaction/slelectkenengxing.do",
				data:{jieduan:jieduan},
				dataType:"json",
				type:"POST",
				success:function (data){
					$("#create-possibility").val(data)
				}
			})
		})

3.要进行参数验证,如果这个参数为空,那么把可能性的文本框设置为空,结束事件

3.controller层接收到请求和参数,去用resourcesB。。属性资源绑定器,去根据传过来的阶段名找概率

    @RequestMapping("/workbench/transaction/slelectkenengxing.do")
    @ResponseBody
    public Object slelectkenengxing(String jieduan){
        ResourceBundle bundle = ResourceBundle.getBundle("kenengxing");
        String string = bundle.getString(jieduan);
        return string;
    }

4.controller返回概率

5.前端ajax回调函数里接收到参数,把它摄入可能性的文本框

6.创建交易页面的客户自动搜索补全(利用插件bs_typeahead自动补全插件)

当容器加载完成后给这个客户的文本框加上插件的方法typeahead

		/*给客户文本框加上自动补全插件函数*/
		$("#create-accountName").typeahead({   
			source:function (Jquery,process){
				$.ajax({
					url:"workbench/transaction/selectBynamekehu.do",
					data:{cstname:Jquery},			//这个搜索下拉列表自动补全参数source:['','','','']	
					dataType:"json",			//传一个这样类型的字符串数组,那么当用户在文本框里输入内容键盘弹起时就会去这个	
					type:"POST",				//字符串里匹配显示到下拉列表中自动补全,这个参数还可以传一个函数	
					success:function (data){	//这个process函数的第一个参数是这个文本框每次键盘弹起获取的值,第二个参数是一个函数的名称		
						process(data)			//可以给这个函数传递一个['','','','']这样的参数,他就会把它给source去显示			
					}		  					//用户每抬起键盘一次就会调用这个source的函数 发ajax请求,controller就会返回这个['','','','']数据,直接调用process(data	                  
				})																	
			}									
		})	

3.当用户每次弹起键盘时收集参数文本内容,发送ajax请求
4.controller接收到参数,去客户表里模糊查询客户名称,返回List(应为这个插件处理的是[‘’,‘’,‘’,‘’]这样的数组)

    @RequestMapping("/workbench/transaction/selectBynamekehu.do")
    @ResponseBody
    public Object selectBynamekehu(String cstname){
        List<String> list = customerServicess.selectByname(cstname);
        return list;
    }
  <select id="selectByname" parameterType="string" resultType="string">
    select name
    from tbl_customer
    where name like concat('%',#{cstname},'%')
  </select>

7.用户点击保存,保存交易信息

1.用户填写完表单中的数据,点击保存,收集参数,发ajax异步请求(因为保存不成功不跳转页面)

			$("#baocunjiaoyi").click(function (){
				/*收集参数*/
				var owner            =$("#create-transactionOwner").val()
				var money          =$("#create-amountOfMoney").val()
				var name             =$("#create-transactionName").val()
				var expectedDate      =$("#create-expectedClosingDate").val()
				var cstname                =$("#create-accountName").val()
				var stage               =$("#create-transactionStage").val()
				var type             =$("#create-transactionType").val()
				var source            =$("#create-clueSource").val()
				var activityId        =$("#yincangactid").val()
				var contactsId       =$("#lianxirenyincang").val()
				var description         =$("#create-describe").val()
				var contactSummary     =$("#create-contactSummary").val()
				var nextContactTime   =$("#create-nextContactTime").val()
				/*表单验证*/

				/*发ajax请求*/
				$.ajax({
					url:"workbench/transaction/baocunTran.do",
					data:{
						owner:owner,
						money:money          ,
						name:name           ,
						expectedDate:expectedDate   ,
						cstname:cstname        ,
						stage:stage          ,
						type:type           ,
						source:source         ,
						activityId:activityId     ,
						contactsId:contactsId     ,
						description:description    ,
						contactSummary:contactSummary ,
						nextContactTime:nextContactTime
					},
					dataType:"json",
					type:"POST",
					success:function (data){
						if(data.code=="1"){
							window.location.href="workbench/transaction/doindex.do"
						}else {
							alert(data.message)
						}
					}

				})

		})

2.controller层收到参数,封装为map(直接写map加上@RequestParam注解),调用service层的方法

    @RequestMapping("/workbench/transaction/baocunTran.do")
    @ResponseBody
    public Object baocunTran(
            @RequestParam   //要使用前端参数名作为key参数值作为value直接封装为map就要用上这个注解
            Map<String,Object> map, HttpSession session){
         map.put(chang.SESSION_NAME,session.getAttribute(chang.SESSION_NAME));
        usercan usercan = new usercan();
        try {
           tranSerivce.insertByTrannnn(map);
           usercan.setCode(chang.CODE_SUCCESS);
       }catch(Exception e){
           e.printStackTrace();
           usercan.setMessage("系统忙请稍后");
           usercan.setCode(chang.CODE_SHIBAI);
        }
        return usercan;
    }

3.在service层要先根据客户名字去查询客户表中有没有这个记录,如果有直接用查到的客户记录的id,如果没有
就要先创建一条客户记录在获取这个新创建的客户记录的id

       String cstname= (String) map.get("cstname");
        User user= (User) map.get(chang.SESSION_NAME);
        Customer customer = customerMapper.selectBycstnameone(cstname);
        if(customer==null){
            customer=new Customer();
            customer.setOwner(user.getId());
            customer.setName(cstname);
            customer.setId(UUIDsaner.getUUID());
            customer.setCreateBy(user.getId());
            customer.setCreateTime(SimDate.getdate(new Date()));
            customerMapper.insertByCustomer(customer);
        }

4.在根据map中的数据封装为交易实体类对象,插入交易表中一条记录

        Tran tran = new Tran();
        tran.setId(UUIDsaner.getUUID());
        tran.setCreateBy(user.getId());
        tran.setCreateTime(SimDate.getdate(new Date()));
        tran.setOwner((String) map.get("owner"));
        tran.setExpectedDate((String) map.get("expectedDate"));
        tran.setContactSummary((String) map.get("contactSummary"));
        tran.setStage((String) map.get("stage"));
        tran.setName((String) map.get("name"));
        tran.setContactsId((String) map.get("contactsId"));
        tran.setActivityId((String) map.get("activityId"));
        tran.setMoney((String) map.get("money"));
        tran.setCustomerId(customer.getId());
        tran.setDescription((String) map.get("description"));
        tran.setNextContactTime((String) map.get("nextContactTime"));
        tran.setSource((String) map.get("source"));
        tran.setType((String) map.get("type"));
        tranMapper.insertByTran(tran);

5.controller层返回响应信息,插入成功还是失败

6.在前端ajax回调函数里面,如果插入成功就跳转到交易主页面,如果失败就不跳转显示提示信息

8.查看交易详细信息

1.用户在交易主页面点击交易名称上的超链接,发送同步请求(因为要跳转页面),参数为这个交易的id

2.controller层收集到id,调用service层的方法,去查询这个交易的详细信息,和这个交易备注的详细信息,和这个交易阶段的历史信息


    @RequestMapping("/workbench/transaction/selectxiangxi.do")
    public String selectxiangxi(String tranId,HttpServletRequest request){
        Tran tran = tranSerivce.selectByTranId(tranId);
        List<TranHistory> tranHistories = tranHisService.selectByTranId(tranId);
        List<TranRemark> remarkList = TravremarkServicess.selectByTranId(tranId);
        /*根据阶段获取可能性 用tran实体类扩展属性传递数据*/
        ResourceBundle kenengxing = ResourceBundle.getBundle("kenengxing");
        tran.setKenengxingzhi( kenengxing.getString(tran.getStage()));
        request.setAttribute("tran",tran);
        request.setAttribute("tranHistories",tranHistories);
        request.setAttribute("remarkList",remarkList);

        return "workbench/transaction/detail";
    }

3.controller层根据阶段获取可能性 用tran实体类扩展属性传递数据,把数据封装到request域中,请求转发到交易详细页面

用到的sql

    select t.id,u1.name as owner,t.money,t.name,t.expected_date,ct.name as customer_id,
dv.value as stage,dv2.value as type,dv3.value as source,act.name as activity_id,
           cts.fullname as contacts_id,u2.name as create_by,t.create_time,
           u3.name as edit_by,t.edit_time,t.description,t.contact_summary,t.next_contact_time

    from tbl_tran as t
    join tbl_user as u1 on u1.id=t.owner
    join tbl_customer as ct on ct.id=t.customer_id
    join tbl_dic_value as dv on dv.id=t.stage
    left join tbl_dic_value as dv2 on dv2.id=t.type
    left join tbl_dic_value as dv3 on dv3.id=t.source
    left join tbl_activity as act on act.id=t.activity_id
    left join tbl_contacts as cts on cts.id=t.contacts_id
    join tbl_user as u2 on u2.id=t.create_by
    left join tbl_user as u3 on u3.id=t.edit_by
    where t.id=#{tranId}

  <select id="selectByTranId" parameterType="string" resultMap="BaseResultMap">
    select hh.id,dv.value as stage,hh.money,hh.expected_date,hh.create_time,u1.name as create_by
    from tbl_tran_history as hh
    join tbl_dic_value as dv on dv.id=hh.stage
    join tbl_user as u1 on u1.id=hh.create_by
    where hh.tran_id=#{tranId}
    order by hh.create_time asc
  </select>

  <select id="selectByTranId" resultMap="BaseResultMap" parameterType="string">
    select a.id,a.note_content,a.create_time,u1.name as create_by,
           a.edit_time,u2.name as edit_by,a.edit_flag
    from tbl_tran_remark as a
           join tbl_user as u1 on a.create_by=u1.id
           left join tbl_user as u2 on a.edit_by=u2.id
    where a.tran_id=#{tranId}
    order by a.create_time asc
  </select>

4.交易详细页面拿到数据用EL表达式和JSTL标签库显示

9.交易详细页面的图标显示

思路:
1.图标分析
图标的颜色:两种
图标的形状:三种
每一个图标对应一个阶段(所以有多少阶段就又多少图标)
图标的顺序:和阶段顺序一样
什么样的阶段显示什么样的图标:首先要去数据库里按顺序查阶段,再在前端用Jstl循环判断显示图标
三种情况:
如果当期遍历出来的阶段和这条交易所在的阶段的order_no相比等于就显示灯泡绿色
如果当期遍历出来的阶段和这条交易所在的阶段的order_no相比小于就显示√绿色
如果当期遍历出来的阶段和这条交易所在的阶段的order_no相比小于就显示点黑色
图标 图标的样式 鼠标停顿显示当前阶段值 颜色
<span class=“glyphicon glyphicon-ok-circle” data-content=“” style=“color: #90F790;”


		<c:forEach items="${stage}" var="stg">
			<c:if test="${stg.orderNo==tran.orderNo}">
				<span class="glyphicon glyphicon-map-marker mystage" data-toggle="popover" data-placement="bottom" data-content="${stg.value}" style="color: #90F790;"></span>
				-----------
			</c:if>
			<c:if test="${stg.orderNo>tran.orderNo}">
				<span class="glyphicon glyphicon-record mystage" data-toggle="popover" data-placement="bottom" data-content="${stg.value}"></span>
				-----------
			</c:if>
			<c:if test="${stg.orderNo<tran.orderNo}">
				<span class="glyphicon glyphicon-ok-circle mystage" data-toggle="popover" data-placement="bottom" data-content="${stg.value}" style="color: #90F790;"></span>
				-----------
			</c:if>
		</c:forEach>

要在controller层调用service层查询所有阶段值,通过request域返回给前端,

        /*查阶段的所有字段 给显示图标用*/
        List<dicvalue> stage = dicvlue.selectBytypecode("stage");

还要查询这个交易的阶段的order_no的值,在查交易的时候多加一个字段和在交易的实体类里扩展一个字段,resourctmap也要变

<result column="order_o" property="orderNo" jdbcType="VARCHAR" />

column查询时起了别名就用别名

十二:CRM用到的插件

前端插件使用步骤

   前端插件使用步骤:
  1)引入开发包:.js,.css
      下载开发包,拷贝到项目webapp目录下
      把开发包引入到jsp文件中:<link><script>
  2)创建容器:<input type="text"><div>
  3)当容器加载完成之后,对容器调用工具函数.

1.前端日历插件基于bootstrap框架(datetimepicker)

		/*给创建市场活动的日期输入框加日历   这是自动加单击事件的*/
		$("#create-startTime,#create-endTime").datetimepicker({
			language:"zh-CN",//设置语言为中文格式
			format:"yyyy-mm-dd",//设置选中完日历后返回给文本框的字符串格式
			minView:"month",//设置最小可以选择到哪里 写月最小显示到日
			autoclose:true,  //设置选完后关闭
			initialDate:new Date(), //设置初始化时候的选中 为当前时间
			todayBtn:true,  //设置在下面显示今天按钮 默认false不显示
			clearBtn:true  //设置清空按钮 默认为false不显示  这里应为要显示中文的清空 所以改了源码bootstrap-theme.min.css这里面
			pickerPosition:'top-right'//在屏幕的上面显示日历
		})

要用到

<link href="jquery/bootstrap_3.3.0/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<link href="jquery/bootstrap-datetimepicker-master/css/bootstrap-datetimepicker.min.css" type="text/css" rel="stylesheet" />

<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
<script type="text/javascript" src="jquery/bootstrap_3.3.0/js/bootstrap.min.js"></script>

<script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/js/bootstrap-datetimepicker.js"></script>
<script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/locale/bootstrap-datetimepicker.zh-CN.js"></script>语言包

2.前端分页插件bs_pagination

<link href="jquery/bootstrap_3.3.0/css/bootstrap.min.css" type="text/css" rel="stylesheet" />
<link href="jquery/bs_pagination-master/css/jquery.bs_pagination.min.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
<script type="text/javascript" src="jquery/bootstrap_3.3.0/js/bootstrap.min.js"></script>
<script type="text/javascript" src="jquery/bs_pagination-master/js/jquery.bs_pagination.min.js"></script>
<script type="text/javascript" src="jquery/bs_pagination-master/localization/en.js"></script>



			/*要在这个函数的ajax的回掉函数里加插件查询函数  应为要获取参数 每页条数和总页数和总记录条数  而且
			* 每发一次请求这个插件的信息要跟着变*/
			$("#fenye").bs_pagination({
				currentPage:beginNo,//当前页号 参数传
				totalRows:data.zongtiao,//总记录条数 ajax返回了
				totalPages:zongye, //总页数是这个函数的必填值 是根据总记录条数 每页显示条数算出来的
				rowsPerPage:pagesize,//每页显示条数
				visiblePageLinks: 5,//这个是那个卡片数最多5个
				showGoToPage: true,//显示跳转部分  默认true显示
				showRowsPerPage: true,//显示设置每页条数 默认true显示
				showRowsInfo: true,//显示记录的信息部分 默认true显示
				onChangePage:function (event,objpage) {
					/*这个是每次页号发生改变就会执行这个函数 第一个参数是这个事件源
					* 第二个参数是这个 页面对象 包含的是外面的函数发生页号改变的所有属性信息
					* 页号发生改变 上面的属性值也会跟着变*/
					//当页号发生改变是 再次调用这个函数 去打请求刷新这个页面 把改变后的当前页号和每页显示条数传给他
					queryhuodong(objpage.currentPage,objpage.rowsPerPage);

				}
			})


/*如果更新成功当前页号和每页显示条数不变 关闭模态窗口 getOption这个函数  这个分页容器#fenye*/
queryhuodong($("#fenye").bs_pagination('getOption','currentPage'),$("#fenye").bs_pagination('getOption','rowsPerPage'))

3.代码搜索自动补全插件bs_typeahead

<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
<script type="text/javascript" src="jquery/bootstrap_3.3.0/js/bootstrap.min.js"></script>
<script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/js/bootstrap-datetimepicker.js"></script>
<script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/locale/bootstrap-datetimepicker.zh-CN.js"></script>
<script type="text/javascript" src="jquery/bs_typeahead/bootstrap3-typeahead.min.js"></script>


2.当容器加载完成后给这个客户的文本框加上插件的方法typeahead

		/*给客户文本框加上自动补全插件函数*/
		$("#create-accountName").typeahead({   
			source:function (Jquery,process){
				$.ajax({
					url:"workbench/transaction/selectBynamekehu.do",
					data:{cstname:Jquery},			//这个搜索下拉列表自动补全参数source:['','','','']	
					dataType:"json",			//传一个这样类型的字符串数组,那么当用户在文本框里输入内容键盘弹起时就会去这个	
					type:"POST",				//字符串里匹配显示到下拉列表中自动补全,这个参数还可以传一个函数	
					success:function (data){	//这个process函数的第一个参数是这个文本框每次键盘弹起获取的值,第二个参数是一个函数的名称		
						process(data)			//可以给这个函数传递一个['','','','']这样的参数,他就会把它给source去显示			
					}		  					//用户每抬起键盘一次就会调用这个source的函数 发ajax请求,controller就会返回这个['','','','']数据,直接调用process(data	                  
				})																	
			}									
		})

4.java文件插件Apache-poi文件的创建

关于办公文档插件使用的基本思想:把办公文档的所有元素封装成普通的Java类,
程序员通过操作这些类达到操作办公文档目的。
文件---------HSSFWorkbook
页-----------HSSFSheet
行-----------HSSFRow
列-----------HSSFCell
样式---------HSSFCellStyle

加依赖apache-poi

 <dependency>
		      <groupId>org.apache.poi</groupId>
		      <artifactId>poi</artifactId>
		      <version>3.15</version>
		    </dependency>

操作类*/
        /*创建一个文件类  往里面加页*/
        HSSFWorkbook sheets = new HSSFWorkbook();
        /*a往页里加行*/
        HSSFSheet sheet = sheets.createSheet("学生表单");
        /*0代表第一行*/
        HSSFRow row = sheet.createRow(0);
        /*往行里加列*/
        HSSFCell cell = row.createCell(0);
        /*给0行0列设置值*/
        cell.setCellValue("学生姓名");
        cell=row.createCell(1);/*设置第二列*/
        cell.setCellValue("学生年龄");
        /*给文件创建样式类*/
        HSSFCellStyle cellStyle = sheets.createCellStyle();

        /*设置对齐样式居中*/
        cellStyle.setAlignment(HorizontalAlignment.CENTER);

        /*循环创建行*/
        for(int i=1;i<=10;i++){
            row=sheet.createRow(i);
            cell = row.createCell(0);
            cell.setCellValue("刘"+i);
            cell= row.createCell(1);
            /*把样式设置到最后一列中*/
            cell.setCellStyle(cellStyle);
            cell.setCellValue(i+10);
        }

        /*输出文件通过文件字节输出流    excel文件是以.xls结尾*/
        FileOutputStream fileOutputStream = new FileOutputStream("E:\aa\ceshi.xls");
        sheets.write(fileOutputStream);
        /*刷新流关闭流 关闭文件*/
        fileOutputStream.flush();
        fileOutputStream.close();
        sheets.close();

5.图表插件echarts

引入开发包
:<%--使用echarts插件他依赖于jquery--%>
    <script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
    <script type="text/javascript" src="jquery/echars/echarts.min.js"></script>

建立容器:
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>

当容器加载完成之后对容器调用插件函数
:            /*插件使用 基于准备好的dom,初始化echarts实例*/
         var macharts=   echarts.init(document.getElementById("main"))
                macharts.setOption(option)

                option = {
                    title: {
                        text: '交易统计阶段数据'
                    },
                    tooltip: {
                        trigger: 'item',
                        formatter: '{a} <br/>{b} : {c}'
                    },
                    toolbox: {
                        feature: {
                            dataView: { readOnly: false },
                            restore: {},
                            saveAsImage: {}
                        }
                    },

                    series: [
                        {
                            name: '数据量',
                            type: 'funnel',
                            left: '10%',
                            width: '80%',
                            label: {
                                formatter: '{b}'
                            },
                            labelLine: {
                                show: true
                            },
                            itemStyle: {
                                opacity: 0.7
                            },
                            emphasis: {
                                label: {
                                    position: 'inside',
                                    formatter: '{b}: {c}'
                                }
                            },
                            data:data
                        }
                    ]
                };

data:数据的格式是这种形式的
[
{value:,name:}{value:,name:}
]

十三:前端通用代码

1.向标签中打入数据

$(“#remarkDiv”).before(html) /在这个标签外部的前面加上摄入的内容/

2,把页面片段显示在动态显示在页面中:
选择器.html(htmlStr):覆盖显示在标签的内部
选择器.text(htmlStr):覆盖显示在标签的内部
选择器.append(htmlStr):追加显示在指定标签的内部的后边
选择器.after(htmlStr):追加显示在指定标签的外部的后边

2.全选框选中与取消

<script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>

        /*给全选按钮加单击事件 全选列表则全选  这种加事件的方式只能给固有元素加事件
        *   固有元素:当调用事件函数给元素添加事件时,如果元素已经生成,则这些元素叫做固有元素;*/
        $("#quanxuan").click(function (){
            /*全选的checked属性是true则 列表的checked属性值也是true*/
            /*获取全部的列表 父子选择器 中间是空格 则可以筛选所有的子标签 中间是>则只能筛选一级字标签 【属性过滤】
            * this代表当前事件元素的dom对象 */
            $("#huodongbiao input[type='checkbox']").prop('checked',this.checked)
        })
        /*给所有列表元素加单击事件  只要有一行没被选中 则全选变暗 如果都全部手动选中 则全选变亮*/
        /*这里因为链表是动态生成的 ajax异步请求不用等待排队 所以当加单击事件时 列表元素还没有生成 所有要用另一种
        * jquery给元素加事件的方式 这种方式既可以给固有元素加也可以给动态生成的元素加*/
        $("#huodongbiao").on('click',"input[type='checkbox']",function () {
            /* 父元素:必须是固有元素,可以直接父元素,也可以是间接父元素.
            * 事件类型  子选择器  函数*/
            //可以获取列表的长度 与列表中被选中的列表长度作比较 如果相等说明全都选中了 反之没有
           if($("#huodongbiao input[type='checkbox']").size()==$("#huodongbiao input[type='checkbox']:checked").size()) {
               $("#quanxuan").prop('checked',true);
           }else{
               $("#quanxuan").prop('checked',false);
           }
        })

3.前端隐藏标签

	<input type="hidden" id="shichangid"> <%--隐藏标签 存市场活动id--%>

4.给元素扩展属性

给元素扩展属性:html页面是可扩展的标记语言,可以给指定的标签任意扩展属性,只要属性名符合标识符的命名规则即可。
两个目的:
1)使用标签保存数据:
如果是表单组件标签,优先使用value属性,只有value不方便使用时,使用自定义属性;
如果不是表单组件标签,不推荐使用value,推荐使用自定义属性。
2)定位标签:
优先考虑id属性,其次考虑name属性,只有id和name属性都不方便使用时,才考虑使用自定义属性。

5.父子选择器与和on函数加事件

使用jquery的on函数 可以给动态生成的元素加事件 也可以给固有元素加事件

父元素必须是固有元素 而且范围越小越好

$(“父元素选择器”).on(“事件名”,“子元素选择器”,function(){})

列子:
$(“#huodongbiao”).on(‘click’,“input[type=‘checkbox’]”,function () {}
标签选择器

/*获取全部的列表 父子选择器 中间是空格 则可以筛选所有的子标签 中间是>则只能筛选一级字标签 【属性过滤】
* this代表当前事件元素的dom对象 */
$(“#huodongbiao input[type=‘checkbox’]”).prop(‘checked’,this.checked)

6.controller返回给前端ajax可以使哪几种数据类型

实体类对象

list集合

map集合

文本数据

xml

7.,ajax向后台发送请求时,可以通过data提交参数,data的数据格式有三种格式:

3,ajax向后台发送请求时,可以通过data提交参数,data的数据格式有三种格式:
1)data:{
k1:v1,
k2:v2,

}
后端controller一个一个参数接 或者实体类接
*劣势:只能向后台提交一个参数名对应一个参数值的数据,
不能向后台提交一个参数名对应多个参数值的数据。
只能向后台提交字符串数据
优势:操作简单
2)data:k1=v1&k2=v2&…
*优势:不但能够向后台提交一个参数名对应一个参数值的数据,
还能向后台提交一个参数名对应多个参数值的数据。
劣势:操作麻烦
只能向后台提交字符串数据
后端controller定义一个参数名相同的数组接
3)data:FormData对象
优势:不但能提交字符串数据,
还能提交二进制数据
劣势:操作更复杂
var a= new FormData()
a.append(“参数名”,“参数值”)
可以追加多个
在ajax里
data:a

最后

以上就是开放眼神为你收集整理的CRM关系管理系统笔记一.CRM项目技术架构介绍二:软件开发的生命周期三:CRM核心业务介绍四:CRM项目所用到的表以及主键字段和表关系简单分析五:项目的规范六:mybatis逆向工程创建数据持久层接口及映射文件七:ssm整合配置八:用户登录功能安全退出的实现九:市场活动功能十:线索模块的功能十一:交易功能的实现十二:CRM用到的插件十三:前端通用代码的全部内容,希望文章能够帮你解决CRM关系管理系统笔记一.CRM项目技术架构介绍二:软件开发的生命周期三:CRM核心业务介绍四:CRM项目所用到的表以及主键字段和表关系简单分析五:项目的规范六:mybatis逆向工程创建数据持久层接口及映射文件七:ssm整合配置八:用户登录功能安全退出的实现九:市场活动功能十:线索模块的功能十一:交易功能的实现十二:CRM用到的插件十三:前端通用代码所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部