每一章东西篇幅太多,每两章分一个来写,有错误或需改正的地方,请指出谢谢。。。
因为是根据大佬的项目点滴做起,如果看到此博客侵犯利益,请告知立即删除。
在完成的品牌管理中,JS和Html的代码都是编辑在一起的,可读性和维护性都交差,并且有些通用的代码可以将其封装起来,
不利于后期的维护。可以运用MVC的设计模式,将代码分离,提高程序的可维护性。
可以在AngularJs中自定义服务,将一些操作封装起来,我们可以在不同的控制器中调用一个服务,这样服务的代码可以被重用。
使用自定义服务,修改品牌管理中的代码。
var app=angular.module('pinyougou', ['pagination']);//定义模块
//定义一个服务brandService
//品牌服务层
app.service('brandService',function($http){
//读取列表数据绑定到表单中
this.findAll=function(){
return $http.get('../brand/findAll.do');
}
//其它方法省略.......
});
//品牌控制层,需要将使用的brandService服务注入进来
app.controller('brandController' ,function($scope,brandService){
//读取列表数据绑定到表单中
$scope.findAll=function(){
brandService.findAll().success(
function(response){
$scope.list=response;
}
);
}
//其它方法省略........
});
我们现在将与后端交互的代码放在了服务层,现在可以将服务层和控制层分离出来,以便调用。
在pinyougou-manager-web工程js下创建base.js
var app=angular.module('pinyougou',[]);
创建base_pagination.js
var app=angular.module('pinyougou',['pagination']);
一个用于不需要分页功能的页面,一个用于需要分页功能的页面.
在pinyougou-manager-web工程js下创建service文件夹。创建brandService.js
//品牌服务层
app.service('brandService',function($http){
//读取列表数据绑定到表单中
this.findAll=function(){
return $http.get('../brand/findAll.do');
}
//其它方法省略........
});
在pinyougou-manager-web的js文件夹下创建brandController.js
//品牌控制层
app.controller('brandController' ,function($scope,brandService){
//读取列表数据绑定到表单中
$scope.findAll=function(){
brandService.findAll().success(
function(response){
$scope.list=response;
}
);
}
//其它方法省略........
});
去掉brand.html原来的JS代码,引入刚才我们建立的JS
有些功能或许每个页面都需要去实现,如分页 复选框选中等功能,对于这些通用的方法,我们可以使用后端常用的继承来进行这些方法的复用。
在pinyougou-manager-web的js/controller目录下建立baseController.js,实现通用的方法
//基本控制层
app.controller('baseController' ,function($scope){
//重新加载列表 数据
$scope.reloadList=function(){
//切换页码
$scope.search( $scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
}
//分页控件配置
$scope.paginationConf = {
currentPage: 1,
totalItems: 10,
itemsPerPage: 10,
perPageOptions: [10, 20, 30, 40, 50],
onChange: function(){
$scope.reloadList();//重新加载
}
};
$scope.selectIds=[];//选中的ID集合
//更新复选
$scope.updateSelection = function($event, id) {
if($event.target.checked){//如果是被选中,则增加到数组
$scope.selectIds.push( id);
}else{
var idx = $scope.selectIds.indexOf(id);
$scope.selectIds.splice(idx, 1);//删除
}
}
});
修改brandController.js
//品牌控制层
app.controller('brandController' ,function($scope,$controller,brandService){
//伪继承,只是将父类的$scope赋给了子类的$scope
$controller('baseController',{$scope:$scope});//继承
//读取列表数据绑定到表单中
$scope.findAll=function(){
brandService.findAll().success(
function(response){
$scope.list=response;
}
);
}
//其它方法省略........
});
//$controller也是angular提供的一个服务,可以实现伪继承,实际上就是与BaseController共享$scope
对于后端的service层 controller层和前端service层 controller层的一些增删改查的方法大致实现一致,我们选择使用代码生成器来生成这样基础的操作(有需要的可以找我要。。。)
tb_specification 规格表
字段 | 类型 | 长度 | 含义 |
Id | Bigint | 主键 | |
Spec_name | varchar | 255 | 规格名称 |
字段 | 类型 | 长度 | 含义 |
Id | Bigint | 主键 | |
Option_name | varchar | 200 | 规格选项名称 |
Spec_id | Bigint | 30 | 规格Id |
Orders | Int | 11 | 排序 |
修改pinyougou-manager-web工程的specification.html
在body元素指定模块名和控制器名
循环表格行
{{entity.id}}
{{entity.specName}}
修改specificationController.js 新增以下代码
//新增选项行
$scope.addTableRow=function(){
//每次触发事件,都会往specificationOptionList中添加一个空对象
//与其绑定的tr,就会多一个对象,从而多一次循环,将文本框遍历出来
$scope.entity.specificationOptionList.push({});
}
specification.html “新建选项”按钮
循环列表行,绑定表格内的编辑框
注意:要修改specification.html “新建”按钮,弹出窗口时对entity进行初始化,否则向集合添加数据时会报错!
实现思路:在每一行将索引值传递给集合,在集合中删除此记录
修改specificationController.js新增以下代码
//批量选项删除
$scope.deleTableRow=function(index){
//删除指定下标的一个元素
$scope.entity.specificationOptionList.splice(index,1);
}
给每行删除按钮增加点击事件
$index在ng-repeat循环指令中自动添加的索引
实现思路:我们将规格和规格选项数据合并成一个对象来传递,这时我们需要用一个对象将这两个对象组合起来。在业务逻辑中,得到组合对象中的规格和规格选项列表,插入规格返回规格ID,然后循环插入规格选项。
(1)我们要增加规格选项,必须要知道新增规格的ID, 所以我们在修改pinyougou-dao 的TbSpecificationMapper.xml ,在insert节点后添加如下配置
SELECT LAST_INSERT_ID() AS id
insert into tb_specification (id, spec_name)
values (#{id,jdbcType=BIGINT}, #{specName,jdbcType=VARCHAR})
(2)在pinyougou-pojo 建立com.pinyougou.pojogroup包,包下建立Specification类
/**
* 规格组合实体类
* @author Administrator
*
*/
public class Specification implements Serializable {
//规格
private TbSpecification specification;
//规格选线列表
private List specificationOptionList;
public TbSpecification getSpecification() {
return specification;
}
public void setSpecification(TbSpecification specification) {
this.specification = specification;
}
public List getSpecificationOptionList() {
return specificationOptionList;
}
public void setSpecificationOptionList(List specificationOptionList) {
this.specificationOptionList = specificationOptionList;
}
}
(3)修改pinyougou-sellergoods-interface的SpecificationService.java
/**
* 增加
*/
public void add(Specification specification);
(4)修改pinyougou-sellergoods-service的SpecificationServiceImpl.java
/**
* 增加
* 此方法效率较低,可直接修改Mapper.xml中的语句,新增批量增加的语句
*/
@Override
public void add(Specification specification) {
specificationMapper.insert(specification.getSpecification());//插入规格
//循环插入规格选项 TODO待修改
for(TbSpecificationOption specificationOption:specification.getSpecificationOptionList()){
specificationOption.setSpecId(specification.getSpecification().getId());//设置规格ID
specificationOptionMapper.insert(specificationOption);
}
}
(5)修改pinyougou-manager-web的SpecificationController.java
/**
* 增加
* @param specification
* @return
*/
@RequestMapping("/add")
public Result add(@RequestBody Specification specification){
try {
specificationService.add(specification);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
(6)修改页面specification.html
绑定规格名称
绑定保存按钮事件
实现思路:通过规格ID,到后端查询规格和规格选项列表,然后通过组合实体类返回结果
(1)修改pinyougou-sellergoods-interface的SpecificationService.java
/**
* 根据ID获取实体
* @param id
* @return
*/
public Specification findOne(Long id);
(2)修改pinyougou-sellergoods-service的SpecificationServiceImpl.java
/**
* 根据ID获取实体
* @param id
* @return
*/
@Override
public Specification findOne(Long id){
//查询规格
TbSpecification tbSpecification = specificationMapper.selectByPrimaryKey(id);
//查询规格选项列表
TbSpecificationOptionExample example=new TbSpecificationOptionExample();
Criteria criteria = example.createCriteria();
criteria.andSpecIdEqualTo(id);//根据规格ID查询
List optionList = specificationOptionMapper.selectByExample(example);
//构建组合实体类返回结果
Specification spec=new Specification();
spec.setSpecification(tbSpecification);
spec.setSpecificationOptionList(optionList);
return spec;
}
(3)修改pinyougou-manager-web的SpecificationController.java
@RequestMapping("/findOne")
public Specification findOne(Long id){
return specificationService.findOne(id);
}
(4)修改页面specification.html 中列表的修改按钮
(1)修改pinyougou-sellergoods-interface的SpecificationService.java
/**
* 修改
*/
public void update(Specification specification);
(2)修改pinyougou-sellergoods-service的SpecificationServiceImpl.java
/**
* 修改
* 对于批量修改规格选项,这里采取的方式是先删除原有的规格选项,然后在全部增加进去
* 因为在编辑页面,很难判断是修改 还是删除 或是新增
* 如果为了追求性能,也可以采取判断属性值,来区分增删改,难度较大。
* 以上只是个人浅薄的见识,望大神批评
*/
@Override
public void update(Specification specification){
//保存修改的规格
specificationMapper.updateByPrimaryKey(specification.getSpecification());//保存规格
//删除原有的规格选项
TbSpecificationOptionExample example=new TbSpecificationOptionExample();
com.pinyougou.pojo.TbSpecificationOptionExample.Criteria criteria = example.createCriteria();
criteria.andSpecIdEqualTo(specification.getSpecification().getId());//指定规格ID为条件
specificationOptionMapper.deleteByExample(example);//删除
//循环插入规格选项,TODO待修改
for(TbSpecificationOption specificationOption:specification.getSpecificationOptionList()){
specificationOption.setSpecId(specification.getSpecification().getId());
specificationOptionMapper.insert(specificationOption);
}
}
(3)修改pinyougou-manager-web的SpecificationController.java
/**
* 修改
* @param specification
* @return
*/
@RequestMapping("/update")
public Result update(@RequestBody Specification specification){
try {
specificationService.update(specification);
return new Result(true, "修改成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "修改失败");
}
}
(4)修改specification.js的save方法
//保存
$scope.save=function(){
var serviceObject;//服务层对象
if($scope.entity.specification.id!=null){//如果有ID
serviceObject=specificationService.update( $scope.entity ); //修改
}else{
serviceObject=specificationService.add( $scope.entity );//增加
}
serviceObject.success(
function(response){
if(response.success){
//重新查询
$scope.reloadList();//重新加载
}else{
alert(response.message);
}
}
);
}
实现思路:我们要删除规格的同时,还要记得将关联的规格选项删除掉。
修改pinyougou-sellergoods-service的SpecificationServiceImpl.java
/**
* 批量删除
*/
@Override
public void delete(Long[] ids) {
//TODO有更优的写法,待修改
for(Long id:ids){
specificationMapper.deleteByPrimaryKey(id);
//删除原有的规格选项
TbSpecificationOptionExample example=new TbSpecificationOptionExample();
com.pinyougou.pojo.TbSpecificationOptionExample.Criteria criteria = example.createCriteria();
criteria.andSpecIdEqualTo(id);//指定规格ID为条件
specificationOptionMapper.deleteByExample(example);//删除
}
}
修改pinyougou-manager-web的specification.html
列表的复选框
删除按钮
首先我们需要理解模板的作用。模板主要有两个:
1是用于关联品牌与规格
2定义扩充属性
tb_type_template 模板表
字段 | 类型 | 长度 | 含义 |
Id | Bigint | 主键 | |
name | varchar | 80 | 模板名称 |
spec_ids | varchar | 1000 | 关联规格(json格式) |
brand_ids | varchar | 1000 | 关联品牌(json格式) |
custom_attribute_items | varchar | 2000 | 扩展属性 |
修改type_template.html ,引入JS
{{entity.id}}
{{entity.name}}
{{entity.brandIds}}
{{entity.specIds}}
{{entity.customAttributeItems}}
在弹出窗口中有个品牌下拉列表,要求品牌是可以选择多个,这与我们之前的单选的下拉列表是不同的。我们要想实现这个功能,需要使用select2 组件来完成。
(1)修改 type_template.html 引入JS
(2)修改typeTemplateController.js ,定义品牌列表数据
$scope.brandList={data:[{id:1,text:'联想'},{id:2,text:'华为'},{id:3,text:'小米'}]};//品牌列表,测试数据
(3)在type_template.html 用select2组件实现多选下拉框
multiple 表示可多选
Config用于配置数据来源
select2-model用于指定用户选择后提交的变量
最终实现效果如下:
我们现在让这个下拉列表的数据从数据库中提取,修改后端代码
(1)pinyougou-dao 工程 ,在TbBrandMapper.xml中添加SQL语句配置
(2)在pinyougou-dao 的TbBrandMapper中添加方法定义
List
(3)修改pinyougou-sellergoods-interface 的BrandService.java,增加方法定义
/**
* 品牌下拉框数据
*/
List
(4)修改pinyougou-sellergoods-service的BrandServiceImpl.java,增加方法
/**
* 列表数据
*/
public List
(5)修改pinyougou-manager-web的BrandController.java
@RequestMapping("/selectOptionList")
public List
(6) 修改pinyougou-manager-web的brandService.js(第九天)
//下拉列表数据
this.selectOptionList=function(){
return $http.get('../brand/selectOptionList.do');
}
(7)修改pinyougou-manager-web的typeTemplateController.js
因为我们在模板控制层中需要使用品牌服务层的方法,所以需要添加依赖注入
//控制层
app.controller('typeTemplateController' ,function($scope,$controller ,typeTemplateService ,brandService){
使用品牌服务方法实现查询,结果赋给变量
$scope.brandList={data:[]};//品牌列表
//读取品牌列表
$scope.findBrandList=function(){
brandService.selectOptionList().success(
function(response){
$scope.brandList={data:response};
}
);
}
(8)修改type_template.html ,添加JS引入
特别注意一下,JS引入的位置,要在typeTemplateController.js之前,因为该控制器要使用到它
(9)修改type_template.html ,添加初始化
在typeTemplateController.js中新增代码
//新增扩展属性行
$scope.addTableRow=function(){
$scope.entity.customAttributeItems.push({});
}
在type_template.html中的“新建”按钮,执行实体的初始化操作
修改“新增扩展属性按钮
循环表格
实现思路:在每一行将索引值传递给集合,在集合中删除。
修改typeTemplateController.js新增以下代码
//删除扩展属性行
$scope.deleTableRow=function(index){
$scope.entity.customAttributeItems.splice(index,1);//删除
}
修改每行的删除按钮
$index 用于获取ng-repeat指令循环中的索引。
修改type_template.html ,绑定文本框
模板名称
保存按钮
修改typeTemplateController.js的findOne方法
//查询实体
$scope.findOne=function(id){
typeTemplateService.findOne(id).success(
function(response){
$scope.entity= response;
$scope.entity.brandIds=JSON.parse($scope.entity.brandIds);//转换品牌列表
$scope.entity.specIds=JSON.parse($scope.entity.specIds);//转换规格列表
$scope.entity.customAttributeItems=JSON.parse($scope.entity.customAttributeItems);//转换扩展属性
}
);
}
从数据库中查询出来的是字符串,我们必须将其转换为json对象才能实现信息的回显。
修改type_template.html
表格中的复选框
删除按钮
我们现在完成的列表中都是以JSON格式显示的,不利于用户的查询。
我们需要将一个json字符串中某个属性的值提取出来,用逗号拼接成一个新的字符串。这样的功能比较常用,所以我们将方法写到baseController.js
//提取json字符串数据中某个属性,返回拼接字符串 逗号分隔
$scope.jsonToString=function(jsonString,key){
var json=JSON.parse(jsonString);//将json字符串转换为json对象
var value="";
for(var i=0;i0){
value+=","
}
value+=json[i][key];
}
return value;
}
页面上使用该函数进行转换
{{jsonToString(entity.brandIds,'text')}}
{{jsonToString(entity.specIds,'text')}}
{{jsonToString(entity.customAttributeItems,'text')}}
(1) 修改pinyougou-manager-web的pom.xml ,添加依赖
org.springframework.security
spring-security-web
org.springframework.security
spring-security-config
(2) 修改web.xml
contextConfigLocation
classpath:spring/spring-security.xml
org.springframework.web.context.ContextLoaderListener
springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
(3) pinyougou-manager-web的spring目录下添加配置文件spring-security.xml
配置说明:
always-use-default-target:指定了是否在身份验证通过后总是跳转到default-target-url属性指定的URL。
authentication-failure-url:指定了身份验证未通过后跳转到指定的URL。
如果你在系统中使用了框架页,需要设置框架页的策略为SAMEORIGIN
修改pinyougou-manager-web的 login.html
说明:
action:/login提交到框架的指定页面
method:必须为POST
input:用户名和密码必须分别为username,password
在pinyougou-manager-web新建LoginController.java
@RestController
@RequestMapping("/login")
public class LoginController {
@RequestMapping("name")
public Map name(){
//通过SpringSecurity获得用户名
String name=SecurityContextHolder.getContext().getAuthentication().getName();
Map map=new HashMap();
map.put("loginName", name);
return map ;
}
}
(1) 新建loginService.js
//登陆服务层
app.service('loginService',function($http){
//读取登录人名称
this.loginName=function(){
return $http.get('../login/name.do');
}
});
(2)新建indexController.js
app.controller('indexController' ,function($scope,$controller ,loginService){
//读取当前登录人
$scope.showLoginName=function(){
loginService.loginName().success(
function(response){
$scope.loginName=response.loginName;
}
);
}
});
页面上引入JS
指令
将页面上的测试用户 替换成 {{loginName}}
在pinyougou-manager-web的spring-security.xml的http节点中添加配置
加此配置后,会自动的产生退出登录的地址/logout,如果你不想用这个地址 ,你也可以定义生成的退出地址以及跳转的页面,配置如下
logout-url:退出的地址,会自动生成
logout-success-url:退出后跳转的地址
修改注销的链接
修改register.html 引入JS
指令
绑定表单(部分代码)
修改sellerController.js ,在保存成功后跳转到登陆页
//保存
$scope.add=function(){
sellerService.add( $scope.entity ).success(
function(response){
if(response.success){
location.href='shoplogin.html';
}else{
alert(response.message);
}
}
);
}
绑定“申请入驻”按钮
申请入驻
修改后端代码pinyougou-sellergoods-service的SellerServiceImpl类的add方法,设置默认状态为0
/**
* 增加
*/
@Override
public void add(TbSeller seller) {
seller.setStatus("0");
seller.setCreateTime(new Date());
sellerMapper.insert(seller);
}
商家申请入驻后,需要网站运营人员在运营商后台进行审核,审核后商家才可以登陆系统。
状态值: 0:未审核 1:已审核 2:审核未通过 3:关闭
修改seller_1.html
引入JS
指令
加入分页控件
循环
{{entity.sellerId}}
{{entity.name}}
{{entity.nickName}}
{{entity.linkmanName}}
{{entity.telephone}}
(1)绑定页面弹出窗口
公司名称
{{entity.name}}
公司手机
{{entity.mobile}}
公司电话
{{entity.telephone}}
公司详细地址
{{entity.addressDetail}}
(2)列表的“详情”按钮
(1)在pinyougou-sellergoods-interface工程的SellerService.java服务接口新增方法定义
/**
* 更改状态
* @param id
* @param status
*/
public void updateStatus(String sellerId,String status);
(2).在pinyougou-sellergoods-service的SellerServiceImpl.java新增方法
public void updateStatus(String sellerId, String status) {
TbSeller seller = sellerMapper.selectByPrimaryKey(sellerId);
seller.setStatus(status);
sellerMapper.updateByPrimaryKey(seller);
}
(3) 在pinyougou-manager-web的SellerController.java新增方法
/**
* 更改状态
* @param sellerId 商家ID
* @param status 状态
*/
@RequestMapping("/updateStatus")
public Result updateStatus(String sellerId, String status){
try {
sellerService.updateStatus(sellerId, status);
return new Result(true, "成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "失败");
}
}
修改pinyougou-manager-web的sellerService.js
//更改状态
this.updateStatus=function(sellerId,status){
return $http.get('../seller/updateStatus.do?sellerId='+sellerId+'&status='+status);
}
修改pinyougou-manager-web的sellerController.js
$scope.updateStatus=function(sellerId,status){
sellerService.updateStatus(sellerId,status).success(
function(response){
if(response.success){
$scope.reloadList();//刷新列表
}else{
alert("失败");
}
}
);
}
修改按钮,调用方法
完成商家系统登陆与安全控制,商家账号来自数据库,并实现密码加密
(1)pom.xml、web.xml 、login.html 参照运营商管理后台
(2)在pinyougou-shop-web创建com.pinyougou.service包,包下创建类UserDetailsServiceImpl.java 实现UserDetailsService 接口
/**
* 认证类
* @author Administrator
*
*/
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List grantedAuths = new ArrayList();
//构建角色列表
grantedAuths.add(new SimpleGrantedAuthority("ROLE_SELLER"));
return new User(username,"123456", grantedAuths);
}
}
(3)在pinyougou-shop-web的spring目录下创建spring-security.xml
经过上述配置,用户在输入密码123456时就会通过(用户名随意)
修改UserDetailsServiceImpl.java ,添加属性和setter方法 ,修改loadUserByUsername方法
/**
* 认证类
* @author Administrator
*
*/
public class UserDetailsServiceImpl implements UserDetailsService {
//与注解的包不在同一包下,采用配置的方式为该属性注入对应的值
private SellerService sellerService;
public void setSellerService(SellerService sellerService) {
this.sellerService = sellerService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("经过了UserDetailsServiceImpl");
//构建角色列表
List grantAuths=new ArrayList();
grantAuths.add(new SimpleGrantedAuthority("ROLE_SELLER"));
//得到商家对象
TbSeller seller = sellerService.findOne(username);
//判断数据库里面是否有此对象,判断该用户是否已经经过审核,
if(seller!=null){
if(seller.getStatus().equals("1")){
//框架自动帮我们做密码校验
return new User(username,seller.getPassword(),grantAuths);
}else{
return null;
}
}else{
return null;
}
}
}
修改pinyougou-shop-web的spring-security.xml ,添加如下配置
经过上述修改后,在登陆页输入用户名和密码与数据库一致即可登陆 。
商家申请入驻的密码要使用BCrypt算法进行加密存储,修改SellerController.java的add方法
@RequestMapping("/add")
public Result add(@RequestBody TbSeller seller){
//密码加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode(seller.getPassword());
seller.setPassword(password);
try {
sellerService.add(seller);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
修改pinyougou-shop-web的spring-security.xml ,添加如下配置
修改认证管理器的配置,框架的解密操作