• 代码“小白”的温故而知新(一)-----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=" 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,                    
     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,  50 },
     22                 { field: 'EmpId', title: '员工编号',  50 },
     23                 { field: 'EmpName', title: '员工姓名',  100 },
     24                 {
     25                     field: 'EmpGender', title: '员工性别',  100, formatter: function (value, row, index) {
     26                         return value ? "" : "";
     27                     }
     28                 },
     29                 { field: 'EmpBirthday', title: '员工生日',  100 },
     30                 { field: 'EmpTelephone', title: '员工电话',  100 },
     31                 { field: 'EmpEmail', title: '邮件地址',  200 },
     32                 {
     33                     field: 'Operator', title: '员工操作',  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");
     74                                     }
     75                                 }
     76                             })
     77                         }
     78                     });
     79                 }
     80             }],
     81             onClickCell: function (rowIndex, field, value) {
     82                 fieldName = field;
     83             },
     84             onSelect: function (rowIndex, rowData) {
     85                 if (fieldName == "Operator") {
     86                     $("#dg").datagrid("unselectRow", rowIndex);
     87                 }
     88             },
     89             onLoadSuccess: function () {
     90                 $(".editemp").click(function () {             
     91                     var empid = $(this).parents("tr").children("td").eq(1).text();
     92                     popEditWindow("编辑员工", 400, "/Employee/AddEmp/"+empid);           
     93                 })
     94             }
     95         });
     96 
     97         //展示员工详细信息
     98         function showEmp(node) {
     99             $(function(){
    100                 var empid = $(node).parents("tr").children().eq(1).text();
    101                 popEditWindow("员工详细信息", 500, "/Employee/ShowEmp/" + empid);
    102             })   
    103         }
    104 
    105         //员工调整弹窗
    106         function AdjustEmp(node) {
    107             $(function () {
    108                 var empid = $(node).parents("tr").children().eq(1).text();
    109                 popEditWindow("员工调整", 400, "/Employee/AdjustEmp/" + empid);
    110             })
    111         }
    112         //分配权限
    113         function SetAction(node) {
    114             $(function () {
    115                 var empid = $(node).parents("tr").children().eq(1).text();
    116                 popEditWindow("分配权限", 400, "/Role/SetRole/" + empid);
    117             })
    118         }
    119     </script>
    120 
    121 </body>
    122 
    123 </html>
    View Code

     【注】:

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

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

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

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

  • 相关阅读:
    Git中从远程的分支获取最新的版本到本地方式
    vector map迭代器失效解决方案
    git 远程库 创建私钥
    centos type.h 编译错误问题
    关于/usr/bin/ld: cannot find -lcrypto 的错误
    线程存储(Thread Specific Data)
    bgcolor RGB 和16进制之间的转换,16进制转RGB,源码
    html 网页代码大全,总结,使用
    使用iframe的好处与坏处详细比拼
    java向MySQL插入当前时间的四种方式和java时间日期格式化的几种方法(案例说明)
  • 原文地址:https://www.cnblogs.com/xrk1203/p/6986447.html
Copyright © 2020-2023  润新知