1、主界面与登录
1.1、主界面的构建
将后台模板下所有的文件都复制到erp_web的webapp目录下
1.2、登录功能实现
(1)登录页面
把登陆页面里的3个文件/文件夹都复制到erp_web的webapp下
修改标题信息,修改login.html的title跟登陆框上的文字信息为“蓝云ERP旗舰版3.1”。
(2)扩展IEmpDao和EmpDao方法
实现按用户名和密码查询实体类功能
Dao接口:
Dao实现:
(3)扩展IEmpBiz接口跟EmpBiz实现
IEmpBiz:
EmpBiz:
(4)添加LoginAction
添加checkUser方法,用于登陆验证
package cn.itcast.erp.action;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.alibaba.fastjson.JSON;
import com.opensymphony.xwork2.ActionContext;
import cn.itcast.erp.biz.IEmpBiz;
import cn.itcast.erp.entity.Emp;
/**
* 实现用户登陆与登出功能
* @author Administrator
*
*/
public class LoginAction {
/** 登陆用户名 */
private String username;
/** 登陆密码 */
private String pwd;
private IEmpBiz empBiz;
/**
* 登陆验证请求
*/
public void checkUser(){
try{
//验证登陆
Emp loginUser = empBiz.findByUsernameAndPwd(username, pwd);
if(null == loginUser){
ajaxReturn(false, "用户名或密码不正确");
return;
}
//保存到session中,表示用户已经登陆了
ActionContext.getContext().getSession().put("loginUser", loginUser);
ajaxReturn(true,"");
}catch(Exception ex){
ex.printStackTrace();
ajaxReturn(false,"登陆失败");
}
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public void setEmpBiz(IEmpBiz empBiz) {
this.empBiz = empBiz;
}
/**
* 返回前端操作结果
* @param success
* @param message
*/
public void ajaxReturn(boolean success, String message){
//返回前端的JSON数据
Map<String, Object> rtn = new HashMap<String, Object>();
rtn.put("success",success);
rtn.put("message",message);
write(JSON.toJSONString(rtn));
}
/**
* 输出字符串到前端
* @param jsonString
*/
public void write(String jsonString){
try {
//响应对象
HttpServletResponse response = ServletActionContext.getResponse();
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出给页面
response.getWriter().write(jsonString);
} catch (IOException e) {
e.printStackTrace();
}
}
}
(5)配置Action
applicationContext-action.xml
<!-- 登陆/退出 -->
<bean id="loginAction" class="cn.itcast.erp.action.LoginAction" scope="prototype">
<property name="empBiz" ref="empBiz"></property>
</bean>
Struts.xml
<!-- 登陆/退出 -->
<action name="login_*" class="loginAction" method="{1}"></action>
(6)修改Login.html
修改文件名,并添加easyUI引用
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>蓝云ERP旗舰版3.1</title>
<link rel="stylesheet" type="text/css" href="css/login.css"/>
<link rel="stylesheet" type="text/css" href="ui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="ui/themes/icon.css">
<script type="text/javascript" src="ui/jquery.min.js"></script>
<script type="text/javascript" src="ui/jquery.easyui.min.js"></script>
<script type="text/javascript" src="ui/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="ui/jquery.serializejson.min.js"></script>
登录表单form修改
给用户名输入框加入name=”username”,给密码输入框加入name=”pwd”
给“登陆”按钮加上id属性,用于登陆方法的绑定
编写ajax方法,实现用户登陆
1.3、显示用户登录名
(1)Action修改
增加showName方法
(2)index.html的修改
添加<span>用于显示登陆用户名
(3)index.js的修改
加入showName方法
/**
* 显示登陆用户名
*/
function showName(){
$.ajax({
url:'login_showName',
method: 'post',
dataType: 'json',
success: function(rtn){
if(rtn.success){
//登陆成功的,则显示登陆用户名称
$('#username').html(rtn.message);
}else{
//没有登陆的,则跑转到登陆页面
location.href="login.html";
}
}
});
}
在index.js的初始化方法中加入showName()方法调用
1.4、退出登录
(1)修改Action
LoginAction中加入loginOut方法
(2)修改index.js
初始化方法中加入loginOut的绑定单击事件,该事件请求退出登陆
//退出登出
$('#loginOut').bind('click',function(){
$.ajax({
url: 'login_loginOut',
success:function(rtn){
location.href="login.html";
}
});
});
2、菜单动态读取
我们之前每做完一个页面,都需要在index.js中增加相应的菜单链接,才能让其在主页菜单中显示出来。有没有更好的实现?让它动态出现呢?
2.1、菜单表的设计
(1)菜单结构分析
分析index.js中的_menus的json数据。可看出菜单里包含icon, menuid, menuname, url, menus数组
(2)设计数据库菜单表
菜单都有一个上级菜单,我们可以通过层级结构进行设计。给每个菜单设置一个上级菜单(pid),就可以实现无限级别的树形(层级)结构的菜单。
查看数据库模型图,
如果数据库还没有建表,则执行下面的建表语句:
CREATE TABLE MYERPUSER.MENU
(
MENUID VARCHAR(30) NOT NULL PRIMARY KEY,
MENUNAME VARCHAR(30) NOT NULL,
URL VARCHAR(200),
ICON VARCHAR(100),
PID VARCHAR(30) NOT NULL
) TABLESPACE MYERP;
COMMENT ON TABLE MYERPUSER.MENU IS '系统菜单';
COMMENT ON COLUMN MYERPUSER.MENU.MENUID IS '编号';
COMMENT ON COLUMN MYERPUSER.MENU.MENUNAME IS '名称';
COMMENT ON COLUMN MYERPUSER.MENU.URL IS '对应URL';
COMMENT ON COLUMN MYERPUSER.MENU.ICON IS '图标样式';
COMMENT ON COLUMN MYERPUSER.MENU.PID IS '上一级菜单编号';
执行添加菜单脚本:
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('0', '系统菜单', '-', 'icon-sys', '-1');
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('100', '基础数据', '-', 'icon-sys', '0');
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('101', '商品类型', 'goodstype.html', 'icon-sys', '100');
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('102', '商品', 'goods.html', 'icon-sys', '100');
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('105', '仓库', 'store.html', 'icon-sys', '100');
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('200', '人事管理', '-', 'icon-sys', '0');
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('201', '部门', 'dep.html', 'icon-sys', '200');
INSERT INTO MENU (MENUID, MENUNAME, URL, ICON, PID) VALUES ('202', '员工', 'emp.html', 'icon-sys', '200');
COMMIT;
2.2、代码实现
(1)实现类:实体代码如下
package cn.itcast.erp.entity;
import java.util.List;
/**
* 菜单实体类
* @author Administrator *
*/
public class Menu {
private String menuid;//菜单ID
private String menuname;//菜单名称
private String icon;//图标
private String url;//URL
//private String pid;//上级菜单ID
private List<Menu> menus;// 子菜单列表
public String getMenuid() {
return menuid;
}
public void setMenuid(String menuid) {
this.menuid = menuid;
}
public String getMenuname() {
return menuname;
}
public void setMenuname(String menuname) {
this.menuname = menuname;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public List<Menu> getMenus() {
return menus;
}
public void setMenus(List<Menu> menus) {
this.menus = menus;
}
}
配置映射文件,添加自关联,使用order-by指定排序
(2)dao层
因为menu的主键为字符串,原来的get方法类型不能匹配,我们需要重载get方法,可以通过字符串主键获取实体
修改IBaseDao,加入T get(String id)方法
修改BaseDao,加入实现
删除MenuDao红框中的代码
(3)Biz层
修改IBaseBiz,添加T get(String id)方法
修改BaseBiz,加入实现
Action层
修改MenuAction,添加getMenuTree方法
0是顶级菜单的ID,通过自关联就可以把所有的菜单都加载进来。
浏览器访问:http://localhost:8080/erp/menu_getMenuTree
运行结果:
(4)前端js修改
修改index.js,使用ajax请求菜单地址来获取菜单数据
3、密码加密
3.1、概述
我们在前期开发阶段,为了便于测试,密码采用明码存储。一旦程序部署到生产环境上,明友存储密码是非常不安全的,必须要对密码进行加密运算。
加密主要分为两种:可逆运算的和不可逆
可逆运算的加密是通过一个秘钥,对一串字符进行加密运算,同样可以通过这个秘钥进行解密运算。
不可逆运算的加密对一串字符进行加密,但是不能还原成原来的字符串。(散列)
那我们接下来做的加密就是使用不可逆加密的MD5加密算法
3.2、添加shiro依赖
修改erp_parent下的POM.xml文件,
定义shiro的版本号
<shiro.ver>1.2.3</shiro.ver>
加入shiro的依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.ver}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.ver}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.ver}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-aspectj</artifactId>
<version>${shiro.ver}</version>
</dependency>
</dependencies>
3.3、代码实现
修改EmpBiz类,添加属性
修改EmpBiz类,重写add方法
修改EmpBiz类的findByUsernameAndPwd方法,查询能对密码进行加密
4、修改密码
4.1、需求分析
每个员工都可以修改自己的密码,输入旧密码、新密码、确认密码后即可修改密码。
点击主页右上方 “修改密码”链接
弹出窗口
旧密码必须正确,新密码与确认密码必须相同,点击保存后更新密码
4.2、后端实现
(1)Dao修改,添加接口方法与实现
IEmpDao修改:
EmpDao实现
在erp_biz中创建自定义异常cn.itcast.erp.ERPException,用于处理业务逻辑出错时抛出
修改EmpBiz
抽出加密方法,修改使用到密码加密的地方都调用该方法
/**
* MD5加密
* @param src 原密码
* @param salt 盐
* @return
*/
private String encrypt(String src, String salt){
Md5Hash md5 = new Md5Hash(src,salt, hashIterations);
return md5.toString();
}
修改加密码加密的地方都改成调用加密方法:
给IEmpBiz和EmpBiz增加修改密码的方法updatePwd
IEmpBiz
在BaseAction中添加获取当前登陆用户的方法
在EmpAction中添加调用修改密码的方法
添加接收新旧密码的属性:
添加updatePwd方法:
4.3、前端实现
(1)修改index.html
修改 修改密码窗口,改成如下:
<!--修改密码窗口-->
<div id="w" class="easyui-dialog">
<div class="easyui-layout" fit="true">
<div region="center" border="false" style="padding: 10px; background: #fff;">
<table cellpadding=3>
<tr>
<td>旧密码:</td>
<td><input id="txtOldPass" type="password" class="txt01" /></td>
</tr>
<tr>
<td>新密码:</td>
<td><input id="txtNewPass" type="password" class="txt01" /></td>
</tr>
<tr>
<td>确认密码:</td>
<td><input id="txtRePass" type="password" class="txt01" /></td>
</tr>
</table>
</div>
</div>
</div>
(2)修改index.js
修改openPwd()方法,把serverLogin的方法都复制到“保存”按钮的handler中,修改后的openPwd代码如下:
//修改密码
function openPwd() {
$('#w').dialog({
title: '修改密码',
width: 300,
modal: true,
closed: true,
height: 192,
buttons:[
{text:'保存',iconCls:'icon-save',handler:function(){
var $oldpass = $('#txtOldPass');
var $newpass = $('#txtNewPass');
var $rePass = $('#txtRePass');
if (!$oldpass.val()) {
$.messager.alert('提示信息','请输入原密码!','info',function(){
$oldpass.select();
})
return false;
}
if (!$newpass.val()) {
$.messager.alert('提示信息','请输入新密码!','info',function(){
$newpass.select();
})
return false;
}
if (!$rePass.val()) {
$.messager.alert('提示信息','请再一次输入密码!','info',function(){
$rePass.select();
})
return false;
}
if ($newpass.val() != $rePass.val()) {
$.messager.alert('提示信息','两次密码不一至!请重新输入','info',function(){
$rePass.select();
})
return false;
}
$.ajax({
url:'emp_updatePwd',
dataType:'json',
type:'post',
data: {oldPwd:$oldpass.val(), newPwd:$newpass.val()},
success:function(rtn){
$.messager.alert('提示',rtn.message,'info',function(){
if(rtn.success){
$oldpass.val('');
$newpass.val('');
$rePass.val('');
$('#w').dialog('close');
}
});
}
});
}},
{text:'取消',iconCls:'icon-cancel',handler:function(){
$('#w').dialog('close');
}}
]
});
}
5、管理员重置密码
5.1、需求分析
当员工忘记密码的时候,管理员可以重置员工的密码。
我们将员工的密码管理功能,从员工管理中独立出来
点击重置密码超链接,弹出窗口,输入新密码,保存更新密码后,关闭窗口
5.2、后端实现
(1)Biz层实现
修改IEmpBiz,增加updatePwd_reset
修改EmpBiz,增加实现
(2)Action层实现
修改EmpAction,增加updatePwd_reset方法,供页面重置密码时调用
5.3、前端实现
(1)新增pwd.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>重置员工密码</title>
<link rel="stylesheet" type="text/css" href="ui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="ui/themes/icon.css">
<script type="text/javascript" src="ui/jquery.min.js"></script>
<script type="text/javascript" src="ui/jquery.easyui.min.js"></script>
<script type="text/javascript" src="ui/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="ui/jquery.serializejson.min.js"></script>
<script type="text/javascript" src="ui/date.js"></script>
<script type="text/javascript" src="js/pwd.js"></script>
</head>
<body>
<table id="grid"></table>
<div id="editDlg" class="easyui-dialog" style="padding:8px;">
<form id="editForm">
<input name="id" type="hidden">
<table>
<tr>
<td>新密码:</td>
<td>
<input id="txtpwd" name="newPwd" type="text">
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
(2)增加pwd.js
在js目录下创建pwd.js
$(function(){
$('#grid').datagrid({
title: '员工信息列表',
url:'emp_listByPage',
columns:[[
{checkbox:true},
{field:'uuid',title:'编号',width:100},
{field:'username',title:'系统登陆名称',width:100},
{field:'name',title:'真实姓名',width:100},
{field:'gender',title:'性别',width:100,formatter:function(gender){
switch(gender){
case 0: return '女';
case 1: return '男';
default: return '未知';
}
}},
{field:'email',title:'电子邮箱地址',width:100},
{field:'tele',title:'联系电话',width:100},
{field:'address',title:'联系地址',width:100},
{field:'birthday',title:'出生年月日',width:100,formatter:function(birthday){
if(birthday){
return new Date(birthday).Format("yyyy-MM-dd");
}
return "";
}},
{field:'dep',title:'部门',width:100,formatter:function(dep){
if(dep){
return dep.name;
}
return "";
}},
{field:'-',title:'操作',width:200,formatter:function(value,row,index){
var operation = '<a href="javascript:void(0)" onclick="updatePwd_reset(' + row.uuid + ')">重置密码</a> ';
return operation;
}}
]],
singleSelect: true,
pagination: true,
rownumbers: true
});
$('#btnSave').bind('click',function(){
if($('#formPwdReset').form('validate') == false){
return;
}
var datas = $('#formPwdReset').serializeJSON();
$.ajax({
url: 'emp_updatePwd_reset',
dataType:'json',
data: datas,
type: 'post',
success: function(rtn){
if(rtn.success){
$.messager.alert("提示信息", "重置密码成功!", 'info', function(){
//关闭窗口
$('#dlgPwdReset').window('close');
//刷新列表
$('#grid').datagrid('reload');
});
}else{
$.messager.alert("提示信息", "重置密码失败!<br>" + rtn.message, 'info');
}
}
});
});
$('#editDlg').dialog({
title:"重置员工密码",
width:260,
height:120,
iconCls:'icon-save',
modal:true,
closed:true,
buttons:[
{
text:'保存',
iconCls: 'icon-save',
handler:function(){
var datas = $('#editForm').serializeJSON();
$.ajax({
url: 'emp_updatePwd_reset',
dataType:'json',
data: datas,
type: 'post',
success: function(rtn){
$.messager.alert("提示", rtn.message, 'info', function(){
if(rtn.success){
//关闭窗口
$('#editDlg').dialog('close');
//刷新列表
$('#grid').datagrid('reload');
}else{
$.messager.alert("提示", "重置密码失败!<br>" + rtn.message, 'info');
}
});
}
});
}
}
]
});
});
function updatePwd_reset(uuid){
//清除表单内容,防止数据混乱
$('#editForm').form('load',{id:uuid,newPwd:""});
//弹出窗口
$('#editDlg').dialog('open');
}
6、员工管理去掉密码修改
6.1、需求分析
我们已经完成了专门的密码管理,那员工管理中的密码部分应该去掉,保证一个统一的入口。
6.2、代码实现
(1)修改emp.html
删除编辑员工中的密码编辑框
问题:当我们修改某个员工后,我们会发现,他的密码也变成空的,这是为什么?怎么解决?有以下3种解决方式
使用触发器,更新的时候,如果密码为空就把旧的密码赋给它
先让对象进入持久化状态,再设置它的属性值
Hbm.xml里加入update=false属性,不更新pwd字段(推荐)
(2)后端代码修改
创建触发器
CREATE OR REPLACE TRIGGER TRI_EMP_UPDATE_PWD
BEFORE
UPDATE OF PWD ON EMP FOR EACH ROW
DECLARE
--声明变量
BEGIN
--如果修改的密码为NULL ,让修改的密码为原密码
--伪记录 :OLD 修改之前的记录 :NEW 修改之后的记录
IF :NEW.PWD IS NULL THEN
:NEW.PWD := :OLD.PWD;
END IF;
END;
设置持久化状态对象的属性
修改EmpBiz,重写update方法
@Override
public void update(Emp emp){
//查出要更新的对象,让其进入持久化状态
Emp dbEmp = this.empDao.get(emp.getUuid());
if(null == dbEmp){
throw new ERPException("没有找到相应的员工!");
}
//修改真实姓名
dbEmp.setName(emp.getName());
//修改联系电话
dbEmp.setTele(emp.getTele());
//修改联系地址
dbEmp.setAddress(emp.getAddress());
//修改出生年月日
dbEmp.setBirthday(emp.getBirthday());
//修改邮箱地址
dbEmp.setEmail(emp.getEmail());
//修改所属部门
dbEmp.setDep(emp.getDep());
}
Hbm.xml里加入update=false属性,不更新pwd字段
修改emp.hbm.xml文件:
7、新增员工设置初始密码
7.1、需求分析
我们在新增员工的时候给他一个默认的密码,密码为登陆名
7.2、实现
修改EmpBiz的新增员工方法
8、静态页面传参数
8.1、需求分析
对于采购的订单,我们需要记录它是从哪一家供应商采购的;对于销售订单,我们也需要记录是卖给哪个客户的。因此我们需要管理供应商及客户的信息
供应商信息管理都包括些什么?主键、名称、地址、联系人、电话等。客户信息管理又包括了哪些呢,也是主键、名称、地址、联系人、电话等。大家可以看到它们两个的字段其实是一样的。遇到这种两对不同类型的对象但其字段又相同的情况下,我们通常是把它们记录在同一个表里,用一个字段来区分。我们这里可以用type来进行区分,1:供应商,2:客户
8.2、实现思路
把相似的信息放入同一个表,可以极大的节省开发时间,降低维护成本,这两个信息对应的实体层、数据访问层、业务层、页面都用同一套代码,只是通过参数进行区分。
我们可以在管理供应商的页面进行修改,通过参数,将此功能一分为二。既可实现供应商管理,又能实现客户管理。
静态页面可传参?Of cause, 我们试试看吧
8.3、代码实现
(1)静态页面传参实现
先做个测试,给supplier.html页面传参数type
If(type==1){
页面标题=供应商管理
}
If(type==2){
页面标题=客户管理
}
将request.js复制到js目录下,并在supplier.html中加入引用
在supplier.html中添加script
if(Request['type']==1){
document.title="供应商管理";
}
if(Request['type']==2){
document.title="客户管理";
}