古人雲:溫故而知新。這是極好的,近來,作為一個小白,利用點空閑時間把之前幾個月自己寫過的一個作為練手的一個OA系統又重新拿來溫習一番,希望在鞏固基礎之上能得到新的啟示。現在回想起來,之前一個人,寫寫停停,不覺感嘆,平時工作中團隊的重要性以及個人力量的渺小。因為是練手的項目,整個系統從資料庫到前端都是 ...
古人雲:溫故而知新。這是極好的,近來,作為一個小白,利用點空閑時間把之前幾個月自己寫過的一個作為練手的一個OA系統又重新拿來溫習一番,希望在鞏固基礎之上能得到新的啟示。現在回想起來,之前一個人,寫寫停停,不覺感嘆,平時工作中團隊的重要性以及個人力量的渺小。因為是練手的項目,整個系統從資料庫到前端都是自己設計,所以不免顯得有點寒磣,不喜勿噴,但該有的重點還是有的,至於美工,哈哈,簡潔也是一種美不是嗎,只能這樣安慰自己了。
準備工作:
1.進行初步的需求分析(有四大板塊:我的桌面,人力資源管理,考勤管理,工作流管理)
2.每個大板塊下又分小塊
(我的桌面---》每日考勤,提交申請,我的審批【高級員工】,我的申請;
人力資源----》員工管理,部門管理,職位管理,角色管理;
考勤管理----》工作日設置,工作時間設置,考勤記錄查詢;
工作流管理----》流程管理)
3.技術實現:ASP.NET MVC,EF,Jquery,T4,log4Net,MD5,Jquery-easy-uI,Sqlser2008,Membercache,後期用到spring.net
先看看前期大概的一個頁面展示圖:
主頁面:
員工信息頁面:
添加員工:
編輯員工:
--------------------------------------------------------------------------------------------------------------------------------------------------------------
正文:
好了,閑話不多說,先設計資料庫吧,在設計資料庫之前,有幾點我們也需要註意,
1.對於表的命名一定要規範;
2.表的設計原則是一張表只記錄一件事,如果表與表有關係的,通過外鍵關聯。
在這裡面,建表需要註意的是對菜單表ActionInfo的設計,因為它分級別,有一級菜單,二級菜單,對於這種表的設計,
ActionId 標識列 主鍵---》(渲染到前端的是id)
Title 標題 nvarchar(20)---》(text)
Leval 1 2 int 決定將來顯示的層級圖標(1代表菜單,2代表菜單項)
URL:允許為空,一級沒有,二級有【通過這個url訪問】
PrentId int not null 對於一級菜單,取值為0【重要】
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
接下來就是搭建框架了,採用簡單抽象工廠三層的項目結構
框架搭完,先做主頁面,Home/Index Home控制下的Index的html代碼 主頁面
1 @using Model.Models 2 @{ 3 Layout = null; 4 EmployeeInfo emp = ViewData["user"] as EmployeeInfo; 5 6 } 7 8 <!DOCTYPE html> 9 10 <html> 11 <head> 12 <meta name="viewport" content="width=device-width" /> 13 <title>通達OA</title> 14 <script src="~/Scripts/jquery-1.7.1.js"></script> 15 <script src="~/Scripts/MyAjaxForm.js"></script> 16 <script src="~/Scripts/jquery.easyui.min.js"></script> 17 <script src="~/Scripts/easyui-lang-zh_CN.js"></script> 18 <link href="~/Content/easyui.css" rel="stylesheet" /> 19 <link href="~/Content/icon.css" rel="stylesheet" /> 20 <style type="text/css"> 21 a 22 { 23 text-decoration:none; 24 } 25 #emp 26 { 27 position:absolute; 28 color:red; 29 bottom:5px; 30 left:5px; 31 32 } 33 34 </style> 35 </head> 36 37 <body class="easyui-layout" onselectstart=" return false;"> 38 <div data-options="region:'north',split:false" style="height: 110px; background: url(/Content/Images/OA2.png) no-repeat 0px -52px ;position:relative"> 39 <p id="emp">歡迎您的登錄:@emp.EmpName <a href="/User/Login?state=false">[註銷用戶]</a></p> 40 </div> 41 42 <div data-options="region:'west',title:'導航菜單',split:false" style="width: 150px; background: url(/Content/Images/OA.jpg) no-repeat -20px "> 43 <ul id="tt"> 44 </ul> 45 </div> 46 47 <div data-options="region:'center',title:'主頁面'" style="padding: 5px; background: url(/Content/Images/OA3.jpg) no-repeat; opacity:0.88"> 48 <div id="p" style="padding: 10px;"> 49 50 </div> 51 </div> 52 53 @* 彈出的獨立視窗 *@ 54 <div id="editwin"> 55 <iframe id="editframe" width="100%" frameborder="0" scrolling="no"> 56 57 </iframe> 58 </div> 59 60 61 <script type="text/javascript"> 62 63 //刷新頁面 64 function afterSave() { 65 $("#editwin").window("close"); 66 $("#editframe").attr("src", "null"); 67 $("#dg").datagrid("reload"); 68 }; 69 70 71 $('#tt').tree({ 72 url: "/Action/LoadData", 73 checkbox: 'true', 74 lines: 'true', 75 dnd: 'false', 76 animate: 'true', 77 formatter: function (node) { 78 return ("[" + node.text + "]"); 79 }, 80 onClick: function (node) { 81 $('#p').panel({ 82 fit:true, 83 title: node.text, 84 href:node.url 85 86 }); 87 } 88 89 }); 90 91 //彈窗 92 function popEditWindow(caption,width,src) 93 { 94 $("#editwin").css("display", "block"); 95 $('#editwin').window({ 96 title: caption, 97 width: width, 98 resizable: false, 99 shadow:false, 100 modal: true 101 }); 102 103 $("#editframe").attr("src", src); 104 $("#editframe").load(function () { 105 var mainheight = $(this).contents().find("body").height() + 30; 106 $(this).height(mainheight); 107 }); 108 109 } 110 111 112 113 </script> 114 </body> 115 </html>View Code
我整體的頁面佈局用的是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==1)) { //第一級菜單 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; } } }View Code
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 = 0; 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 = "888888"; 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"); } } }View Code
給出Employee的List的視圖代碼,後面部門類似參考
1 @{ 2 Layout = null; 3 } 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta name="viewport" content="width=device-width" /> 8 <title>員工界面</title> 9 </head> 10 <body> 11 <table id="dg"> 12 </table> 13 <script type="text/javascript"> 14 var fieldName; 15 $("#editwin").css("display", "none"); 16 $('#dg').datagrid({ 17 url: '/Employee/LoadData', 18 pagination: true,//page=1&rows=10 19 pageList: [10, 15, 20], 20 columns: [[ 21 { field: 'check', checkbox: true, width: 50 }, 22 { field: 'EmpId', title: '員工編號', width: 50 }, 23 { field: 'EmpName', title: '員工姓名', width: 100 }, 24 { 25 field: 'EmpGender', title: '員工性別', width: 100, formatter: function (value, row, index) { 26 return value ? "女" : "男"; 27 } 28 }, 29 { field: 'EmpBirthday', title: '員工生日', width: 100 }, 30 { field: 'EmpTelephone', title: '員工電話', width: 100 }, 31 { field: 'EmpEmail', title: '郵件地址', width: 200 }, 32 { 33 field: 'Operator', title: '員工操作', width: 300, 34 formatter: function () { 35 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> "; 36 } 37 } 38 39 ]], 40 toolbar: [{ 41 iconCls: 'icon-add', 42 text: '添加員工', 43 handler: function () { 44 //調用在主頁面Index中封裝的彈窗函數 45 popEditWindow("添加員工", 400, "/Employee/AddEmp"); 46 } 47 }, '-', { 48 iconCls: 'icon-cancel', 49 text: '刪除員工', 50 handler: function () { 51 var rows = $("#dg").datagrid('getSelections'); 52 if (rows.length == 0) { 53 $.messager.alert("提示", "請選擇刪除行!"); 54 return; 55 } 56 $.messager.confirm('確認', '確定刪除嗎?', function (r) { 57 if (r) { 58 //獲取編號id,並以一定的規則做成字元串 59 var idlist = ""; 60 for (var i = 0; i < rows.length; i++) { 61 idlist = idlist + rows[i]["EmpId"] + ","; 62 } 63 //截取 64 idlist = idlist.substr(0, idlist.length - 1); 65 //非同步請求發送要刪除的字元串 66 $.ajax({ 67 url: "/Employee/Delete", 68 type: "post", 69 data: { "idlist": idlist }, 70 dataType: "text", 71 success: function (res) { 72 if (res == "ok") { 73 $("#dg").datagrid("reload"