代码“小白”的温故而知新(一)-----OA管理系统

时间:2022-03-13 01:56:16

古人云:温故而知新。这是极好的,近来,作为一个小白,利用点空闲时间把之前几个月自己写过的一个作为练手的一个OA系统又重新拿来温习一番,希望在巩固基础之上能得到新的启示。现在回想起来,之前一个人,写写停停,不觉感叹,平时工作中团队的重要性以及个人力量的渺小。因为是练手的项目,整个系统从数据库到前端都是自己设计,所以不免显得有点寒碜,不喜勿喷,但该有的重点还是有的,至于美工,哈哈,简洁也是一种美不是吗,只能这样安慰自己了。

准备工作:

1.进行初步的需求分析(有四大板块:我的桌面,人力资源管理,考勤管理,工作流管理)

2.每个大板块下又分小块

(我的桌面---》每日考勤,提交申请,我的审批【高级员工】,我的申请;

人力资源----》员工管理,部门管理,职位管理,角色管理;

考勤管理----》工作日设置,工作时间设置,考勤记录查询;

工作流管理----》流程管理)

3.技术实现:ASP.NET  MVC,EF,Jquery,T4,log4Net,MD5,Jquery-easy-uI,Sqlser2008,Membercache,后期用到spring.net

先看看前期大概的一个页面展示图:

主页面:

代码“小白”的温故而知新(一)-----OA管理系统

员工信息页面:

代码“小白”的温故而知新(一)-----OA管理系统

添加员工:

代码“小白”的温故而知新(一)-----OA管理系统

编辑员工:

代码“小白”的温故而知新(一)-----OA管理系统

--------------------------------------------------------------------------------------------------------------------------------------------------------------

正文:

好了,闲话不多说,先设计数据库吧,在设计数据库之前,有几点我们也需要注意,

1.对于表的命名一定要规范;

2.表的设计原则是一张表只记录一件事,如果表与表有关系的,通过外键关联。

在这里面,建表需要注意的是对菜单表ActionInfo的设计,因为它分级别,有一级菜单,二级菜单,对于这种表的设计,

ActionId     标识列  主键---》(渲染到前端的是id)

Title   标题   nvarchar(20)---》(text)

Leval  1  2 int   决定将来显示的层级图标(1代表菜单,2代表菜单项)

URL:允许为空,一级没有,二级有【通过这个url访问】

PrentId  int   not null  对于一级菜单,取值为0【重要】

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来就是搭建框架了,采用简单抽象工厂三层的项目结构

代码“小白”的温故而知新(一)-----OA管理系统

框架搭完,先做主页面,Home/Index    Home控制下的Index的html代码  主页面

 @using Model.Models
@{
Layout = null;
EmployeeInfo emp = ViewData["user"] as EmployeeInfo; } <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>通达OA</title>
<script src="~/Scripts/jquery-1.7.1.js"></script>
<script src="~/Scripts/MyAjaxForm.js"></script>
<script src="~/Scripts/jquery.easyui.min.js"></script>
<script src="~/Scripts/easyui-lang-zh_CN.js"></script>
<link href="~/Content/easyui.css" rel="stylesheet" />
<link href="~/Content/icon.css" rel="stylesheet" />
<style type="text/css">
a
{
text-decoration:none;
}
#emp
{
position:absolute;
color:red;
bottom:5px;
left:5px; } </style>
</head> <body class="easyui-layout" onselectstart=" return false;">
<div data-options="region:'north',split:false" style="height: 110px; background: url(/Content/Images/OA2.png) no-repeat 0px -52px ;position:relative">
<p id="emp">欢迎您的登录:@emp.EmpName <a href="/User/Login?state=false">[注销用户]</a></p>
</div> <div data-options="region:'west',title:'导航菜单',split:false" style="width: 150px; background: url(/Content/Images/OA.jpg) no-repeat -20px ">
<ul id="tt">
</ul>
</div> <div data-options="region:'center',title:'主页面'" style="padding: 5px; background: url(/Content/Images/OA3.jpg) no-repeat; opacity:0.88">
<div id="p" style="padding: 10px;"> </div>
</div> @* 弹出的独立窗口 *@
<div id="editwin">
<iframe id="editframe" width="100%" frameborder="" scrolling="no"> </iframe>
</div> <script type="text/javascript"> //刷新页面
function afterSave() {
$("#editwin").window("close");
$("#editframe").attr("src", "null");
$("#dg").datagrid("reload");
}; $('#tt').tree({
url: "/Action/LoadData",
checkbox: 'true',
lines: 'true',
dnd: 'false',
animate: 'true',
formatter: function (node) {
return ("[" + node.text + "]");
},
onClick: function (node) {
$('#p').panel({
fit:true,
title: node.text,
href:node.url });
} }); //弹窗
function popEditWindow(caption,width,src)
{
$("#editwin").css("display", "block");
$('#editwin').window({
title: caption,
width: width,
resizable: false,
shadow:false,
modal: true
}); $("#editframe").attr("src", src);
$("#editframe").load(function () {
var mainheight = $(this).contents().find("body").height() + ;
$(this).height(mainheight);
}); } </script>
</body>
</html>

我整体的页面布局用的是Easy-UI,首页内容使用的是panel组件,菜单栏使用的是tree组件。

需要注意的是:

1.panel组件可以通过设置href属性从远程加载页面,但只会加载页面的body内容,可是像添加员工,编辑员工这样的,我们需要额外进行一些css样式的改变时,他却变得无效了,所以在面板之内,我添加了一个Iframe框架,Iframe  框架  有一个src属性,这个属性可以请求一个远程界面(完整的,独立的页面),你可以在里面做上你自己的css样式进行改变。

2.iframe框架如何自适应高度?

解决方案:

$("#editframe").load(function () {

var mainheight = $(this).contents().find("body").height() + 30;

$(this).height(mainheight);

});

3.如何在iframe框架内嵌的网页中去访问他所在的主页面的资源?

解决方案:

在主页面定义要使用的资源方法,然后再包含iframe的子页面中调用window.parent.方法名()。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Action/Index   ||菜单栏代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using BLL;
using Model.Models; namespace UI.Controllers
{
public class ActionController :BaseController
{
//
// GET: /Action/ public ActionResult Index()
{
return View();
}
/// <summary>
/// 一次性加载菜单数据
/// </summary>
///
/// <returns>
/// 符合tree树形控件的json格式的对象
/// </returns>
public ActionResult LoadData()
{
//没有进行权限分配,默认登录后加载所有菜单列表
List<ActionInfo> actionList = new ActionService().GetActionList(a => true); //调用存储过程进行过滤
// List<ActionInfo> actionList = new ActionService().GetActionListByEmp(this.user.EmpId); //采用EF查询进行过滤
//List<ActionInfo> actionList = new ActionService().GetActionByEmpId(this.user.EmpId); //构造符合tree树形控件的json格式的对象
List<MenuItem> menulist = new List<MenuItem>();
foreach (var item in actionList.Where(a=>a.Leval==))
{
//第一级菜单
MenuItem first = new MenuItem { id = item.ActionId, text = item.Title, state = "closed", url = null };
List<MenuItem> second = new List<MenuItem>();
List<ActionInfo> secondActionList = actionList.Where(a=>a.PrentId==item.ActionId).ToList();
foreach (var i in secondActionList)
{
second.Add(new MenuItem { id = i.ActionId, text = i.Title,state="open",url = i.URL });
}
first.children = second;
menulist.Add(first);
}
//JSON序列化
JavaScriptSerializer jss = new JavaScriptSerializer();
string result = jss.Serialize(menulist);
return Content(result);
} } //构造符合tree组件的实体类
public class MenuItem
{
public int id { get; set; }
public string text { get; set; }
public string state { get; set; }
public string url { get; set; }
public List<MenuItem> children { get; set; }
}
}

Employee     ||控制器下代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Model.Models;
using BLL;
using UI.Models;
using System.Web.Script.Serialization;
using Newtonsoft.Json; namespace UI.Controllers
{
public class EmployeeController : BaseController
{
//
// GET: /Employee/ public ActionResult List()
{
return View();
} public ActionResult LoadData(int page, int rows)
{ int totalCount = ;
List<EmployeeInfo> emplist = new EmployeeService().GetEmpListByPage(page, rows, ref totalCount);
//解决方案二:使用Newtonsoft程序集
var result = JsonConvert.SerializeObject(new { total = totalCount, rows = emplist });
return Content(result); } [HttpGet]
public ActionResult AddEmp(int? id)
{
string url = "/Employee/AddEmp";
List<DepartmentInfo> depList = new DepartmentService().GetDepList(d => true);
List<PositionInfo> posList = new PositionService().GetPosList(p => true);
EmployeeInfo emp;
ViewEmpModel model = new ViewEmpModel();
if (id != null)
{
url = "/Employee/Update";
emp = new EmployeeService().GetDepByEmp(e => e.EmpId == id).SingleOrDefault();
model = new ViewEmpModel { EmpName = emp.EmpName, EmpBirthday = emp.EmpBirthday.ToString(), EmpEmail = emp.EmpEmail, EmpTelephone = emp.EmpTelephone, EmpUrl = url, LoginId = emp.LoginId, EmpGender = emp.EmpGender, DepId = emp.DepId, EmpId = emp.EmpId, PosId = emp.PosId, LoginPwd = emp.LoginPwd, DelFlag = emp.DelFlag };
ViewData["DepId"] = new SelectList(depList, "DepId", "DepName", emp.DepId);
ViewData["PosId"] = new SelectList(posList, "PosId", "PosName", emp.PosId);
}
else
{
ViewData["DepId"] = new SelectList(depList, "DepId", "DepName");
ViewData["PosId"] = new SelectList(posList, "PosId", "PosName");
ViewData["deplist"] = depList;
ViewData["poslist"] = posList;
}
ViewData.Model = model;
return View();
} [HttpPost]
public ActionResult AddEmp(EmployeeInfo emp)
{
emp.DelFlag = false;
emp.LoginPwd = "";
bool flag = new EmployeeService().AddEmp(emp);
return flag ? Content("ok") : Content("fail");
} //展示员工详细信息
public ActionResult ShowEmp(int id)
{
EmployeeInfo emp = new EmployeeService().GetDepByEmp(e => e.EmpId == id).SingleOrDefault(); List<AdjustPosition> adplist = new AdjustPositionService().GetAdpList(a => a.EmpId == emp.EmpId);
List<AdjustDepartment> addlist = new AdjustDepartmentService().GetAddList(a => a.EmpId == emp.EmpId); string gender = emp.EmpGender ? "女" : "男";
ViewData["gender"] = gender;
ViewData["adp"] = adplist;
ViewData["add"]=addlist;
return View(emp);
} //修改员工
[HttpPost]
public ActionResult Update(EmployeeInfo emp)
{
bool falg = new EmployeeService().UpdateEmp(emp);
return falg ? Content("ok") : Content("fail");
}
/// <summary>
/// 删除员工
/// </summary>
/// <param name="idlist"></param>
/// <returns></returns>
public ActionResult Delete(string idlist)
{
bool falg = new EmployeeService().DeleteEmpList(idlist);
return falg ? Content("ok") : Content("fail");
} //员工调整
[HttpGet]
public ActionResult AdjustEmp(int id)
{
List<DepartmentInfo> depList = new DepartmentService().GetDepList(d => true);
List<PositionInfo> posList = new PositionService().GetPosList(p => true);
EmployeeInfo emp = new EmployeeInfo();
emp = new EmployeeService().GetDepByEmp(e => e.EmpId == id).SingleOrDefault();
//如何将查询到的数据绑定到视图中的下拉列表框,第四个参数是选中的值
ViewData["DepId"] = new SelectList(depList, "DepId", "DepName", emp.DepId);
ViewData["PosId"] = new SelectList(posList, "PosId", "PosName", emp.PosId);
ViewData["deplist"] = depList;
ViewData["poslist"] = posList;
ViewData.Model = emp; ViewData["EmpId"]=emp.EmpId;
ViewData["OldDepartmentId"]=emp.DepId;
ViewData["OldPositionId"] = emp.PosId;
return View();
} [HttpPost]
public ActionResult Adjust(EmployeeInfo emp,AdjustDepartment add,AdjustPosition adp)
{
//接收原来部门的编号
int OldDepartmentId = Convert.ToInt32(Request["OldDepartmentId"]);
//接收原来职位的编号
int OldPositionId = Convert.ToInt32(Request["OldPositionId"]); AdjustManagerService am = new AdjustManagerService();
adp.NewPositionId = emp.PosId;
add.NewDepartmentId = emp.DepId;
adp.AdjustTime = DateTime.Now;
add.AdjustTime = DateTime.Now;
bool falg;
//职位调整并且部门没调整
if (add.NewDepartmentId == add.OldDepartmentId&&adp.NewPositionId != adp.OldPositionId)
{ falg = am.Add(adp);
}
//部门调整并且职位没调整
else if (add.NewDepartmentId != add.OldDepartmentId && adp.NewPositionId == adp.OldPositionId)
{
falg = am.Add(add);
}
//部门和职位都调整了
else if (add.NewDepartmentId != add.OldDepartmentId && adp.NewPositionId != adp.OldPositionId)
{
falg = am.Add(add,adp);
}
falg = new EmployeeService().UpdateEmp(emp);
return falg ? Content("ok") : Content("fail"); }
}
}

给出Employee的List的视图代码,后面部门类似参考

 @{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>员工界面</title>
</head>
<body>
<table id="dg">
</table>
<script type="text/javascript">
var fieldName;
$("#editwin").css("display", "none");
$('#dg').datagrid({
url: '/Employee/LoadData',
pagination: true,//page=1&rows=10
pageList: [, , ],
columns: [[
{ field: 'check', checkbox: true, width: },
{ field: 'EmpId', title: '员工编号', width: },
{ field: 'EmpName', title: '员工姓名', width: },
{
field: 'EmpGender', title: '员工性别', width: , formatter: function (value, row, index) {
return value ? "女" : "男";
}
},
{ field: 'EmpBirthday', title: '员工生日', width: },
{ field: 'EmpTelephone', title: '员工电话', width: },
{ field: 'EmpEmail', title: '邮件地址', width: },
{
field: 'Operator', title: '员工操作', width: ,
formatter: function () {
return "<a href='#' class='editemp'>编辑员工</a> | <a href='#' class='detail' onclick='showEmp(this);'>详细信息</a> |<a href='#' class='adjustemp' onclick='AdjustEmp(this)'>员工调整</a> |<a href='#' class='setAction' onclick='SetAction(this)'>分配权限</a> ";
}
} ]],
toolbar: [{
iconCls: 'icon-add',
text: '添加员工',
handler: function () {
//调用在主页面Index中封装的弹窗函数
popEditWindow("添加员工", , "/Employee/AddEmp");
}
}, '-', {
iconCls: 'icon-cancel',
text: '删除员工',
handler: function () {
var rows = $("#dg").datagrid('getSelections');
if (rows.length == ) {
$.messager.alert("提示", "请选择删除行!");
return;
}
$.messager.confirm('确认', '确定删除吗?', function (r) {
if (r) {
//获取编号id,并以一定的规则做成字符串
var idlist = "";
for (var i = ; i < rows.length; i++) {
idlist = idlist + rows[i]["EmpId"] + ",";
}
//截取
idlist = idlist.substr(, idlist.length - );
//异步请求发送要删除的字符串
$.ajax({
url: "/Employee/Delete",
type: "post",
data: { "idlist": idlist },
dataType: "text",
success: function (res) {
if (res == "ok") {
$("#dg").datagrid("reload");
}
}
})
}
});
}
}],
onClickCell: function (rowIndex, field, value) {
fieldName = field;
},
onSelect: function (rowIndex, rowData) {
if (fieldName == "Operator") {
$("#dg").datagrid("unselectRow", rowIndex);
}
},
onLoadSuccess: function () {
$(".editemp").click(function () {
var empid = $(this).parents("tr").children("td").eq().text();
popEditWindow("编辑员工", , "/Employee/AddEmp/"+empid);
})
}
}); //展示员工详细信息
function showEmp(node) {
$(function(){
var empid = $(node).parents("tr").children().eq().text();
popEditWindow("员工详细信息", , "/Employee/ShowEmp/" + empid);
})
} //员工调整弹窗
function AdjustEmp(node) {
$(function () {
var empid = $(node).parents("tr").children().eq().text();
popEditWindow("员工调整", , "/Employee/AdjustEmp/" + empid);
})
}
//分配权限
function SetAction(node) {
$(function () {
var empid = $(node).parents("tr").children().eq().text();
popEditWindow("分配权限", , "/Role/SetRole/" + empid);
})
}
</script> </body> </html>

【注】:

常见错误:检测到序列化循环引用

解决方案一:创建一个VO模型对象,不包含导航属性;

解决方法二:推荐使用牛顿JSON程序集

好了,今天的复习就到这。期待下一节的“温故而知新(二)”。