员工请假管理系统(MFC+ACCESS数据库+ODBC数据源)

主要界面:(未添加皮肤)

登陆界面:



主界面:


1.题目要求

用MFC和ACCESS数据库设计一个请假管理系统可以进行对员工请假的管理和对公司假期及国定假期的设定。

2.功能需求

      2.1.系统管理

  1.        更换登录用户
  2.        退出

    2.2.请假管理窗体

  1.        员工请假登记
  2.        删除请假记录
  3.        撤销员工假期
  4.        员工请假记录
  5.        部门请假记录

   2.3.假期设置窗体

  1.        设定国定假日
  2.        公司策略设定(年假数)
  3.        设置公司部门(增加、删除、修改)
  4.        管理员工信息(增加、删除、修改)

   2.4.数据库管理

  1.        数据库备份
  2.        数据库恢复

3. 总体设计

3.1 系统模块

3.1.1请假管理系统功能模块图如下:




3.2 系统业务处理流程

3.2.1以管理员身份为例,请假管理系统流程图如下:




3.3 数据库设计

这里使用ACCESS2003数据库,数据库名字为StaffDBQ.mdb,其中包括7个表,如下:

 

表1:already

字段名称

数据类型

说明

ID

文本

员工编号

day

数字

员工已用年假数

 

表2:LeaveRecords

字段名称

数据类型

说明

ID

文本

员工编号

name

文本

员工姓名

reason

文本

请假原因

day

数字

请假天数

start

日期/时间

开始时间

end

日期/时间

结束时间

yearday

是/否

申请年假

dept

文本

所属部门

time

数字

第几次请假

 

表3:SetCompany

字段名称

数据类型

说明

allday

数字

年假天数

post

文本

职位

 

表4:SetCountry

字段名称

数据类型

说明

name

文本

假日名称

day

数字

放假天数

start

日期/时间

开始时间

end

日期/时间

结束时间

 

表5:SetDepartment

字段名称

数据类型

说明

deptname

文本

部门名称

 

表6:StaffInfo

字段名称

数据类型

说明

ID

文本

员工编号

name

文本

员工姓名

sex

文本

员工性别

age

数字

员工年龄

tel

文本

员工电话

addr

文本

员工住址

dept

文本

员工部门

post

文本

员工职位

 

表7:user

字段名称

数据类型

说明

username

文本

用户名称

password

文本

用户密码

right

数字

用户权限


4.详细设计

在这次的请假管理系统课程设计中,由于很多界面实现的原理都差不多,所以就不把每个界面是怎么实现的一一描述出来了,只是详细的描述要怎么实现某个效果。

4.1数据库的自动关联

为了避免在别的电脑上面运行要手动关联数据库的麻烦,我们这里自动关联数据源。代码添加位置:App类的InitInstance()函数中。

    //自动关联数据源

     SQLConfigDataSource(NULL,ODBC_ADD_DSN,

         "Microsoft Access Driver(*.mdb)",

         "DSN=StaffDSN\0"

        "DBQ=.\\StaffDBQ.mdb\0"

         );

这里用到了SQLConfigDataSource()函数,其中StaffDSN为数据源名字,StaffDBQ.mdb为数据库名字。注意用此函数的时候,需要添加头文件#include"odbcinst.h"同时在工程->设置中添加odbccp32.lib链接库。

4.2登陆功能

程序一运行会首先进入App类的初始化函数InitInstance()中,所以我们在该函数中弹出登陆界面。

//用户登陆

CLoginDlgloginDlg;

if(loginDlg.DoModal()!=IDOK)

     return FALSE;

DoModal()会返回点击按钮的ID值,登陆原理:在登陆界面的登陆按钮(IDOK)下面添加登陆验证代码,首先检测账号是否为空,再检测密码是否为空。如果都不为空,则查找数据库里面的账号密码是否匹配。如果匹配,传递用户的权限,进入系统。

voidCLoginDlg::OnOK()

{

     CUserSet recordset;

     CString strSQL;

     UpdateData(TRUE);

     if(m_strUser.IsEmpty())

     {

         MessageBox("请输入用户名!","登陆错误");

         m_ctrUser.SetFocus();

         return;

     }

     //检查密码是否输入

     if(m_strPass.IsEmpty())

     {

         MessageBox("请输入密码!","错误");

         m_ctrPass.SetFocus();

         return;

     }

     CStaffVactionApp*  ptheApp = (CStaffVactionApp *) AfxGetApp();

     strSQL.Format("select * from userwhere username='%s' AND password='%s'",m_strUser,m_strPass);

 

     if(!recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL))

     {

         MessageBox("打开数据库失败!","数据库错误",MB_OK);

         return;

     }   

     if(recordset.GetRecordCount()==0)

     {

         recordset.Close();

         MessageBox("密码错误,请重新输入!","错误");

         m_strPass="";

         m_ctrPass.SetFocus();

         UpdateData(FALSE);

     }

     else

     {

         ptheApp->m_nRight =recordset.m_right;    //传递权限

         recordset.Close();

         CDialog::OnOK();

     }

     //CDialog::OnOK();

}

4.3如何给下拉控件添加下拉列表

这里就拿用户登陆界面的下拉列表来分析,效果如下图:


由于要在对话框一弹出来就有下拉列表,所以需要将添加下拉列表的代码添加在对话框的初始化函数OnInitDialog()中。如果没有这个函数,可以在类向导里面添加WM_INITDIALOG消息响应事件。

BOOLCLoginDlg::OnInitDialog()

{

     CDialog::OnInitDialog();

     //将登陆用户名加载到列表下拉框

     CUserSet recordset;

     CString strSQL;

     UpdateData(TRUE);

     strSQL="select * from user";

     if(!recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL))

     {

         MessageBox("打开数据库失败!","数据库错误",MB_OK);

         return FALSE;

     }   

     while(!recordset.IsEOF())

     {

         m_ctrUser.AddString(recordset.m_username);

         recordset.MoveNext();

     }

     recordset.Close();

    

     return TRUE;  // return TRUE unless you set the focus to acontrol

                   // EXCEPTION: OCX Property Pagesshould return FALSE

}

其中m_ctrUser是下拉控件通过类型导关联的一个变量,m_username为数据库里面用户的名字,这里主要用到AddString()函数往下拉控件里面添加下拉项。

 

4.4如何在列表控件中加载列表

这里用设定国定假日来举例,添加列表后,效果如下图:


首先在对话框上拖放一个列表控件,然后点击->属性->样式->查看(报告)->排列(顶端)。

由于要在对话框产生的时候就显示列表,同样要将代码添加在对话框的初始化函数OnInitDialog()中。

BOOLCSetCountryDlg::OnInitDialog()

{

     CDialog::OnInitDialog();

 

     CSetCountrySet m_recordset;

     CString strSQL;

     UpdateData(TRUE);

     strSQL="select * fromSetCountry";

     if(!m_recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL))

     {

         MessageBox("打开数据库失败!","数据库错误",MB_OK);

         return FALSE;

     }   

     while(!m_recordset.IsEOF())

     {

         m_ctrName.AddString(m_recordset.m_name);  //添加假日名称下拉列表数据

         m_recordset.MoveNext();

     }

     m_recordset.Close();  //关闭记录集

     //添加左边列表栏数据

     //创建用户列表

     m_ctrListName.InsertColumn(0,"假日名称");

     m_ctrListName.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);

     m_ctrListName.SetColumnWidth(0,110);  //第0列宽度110

     //在添加用户列表中添加用户名

     RefreshData();

     return TRUE;  // return TRUE unless you set the focus to acontrol

                   // EXCEPTION: OCX Property Pagesshould return FALSE

}

voidCSetCountryDlg::RefreshData()

{

     m_ctrListName.SetFocus();         //设置控件焦点

     m_ctrListName.DeleteAllItems();   //清空用户列表

     m_ctrListName.SetRedraw(FALSE);   //重绘

 

     CString strSQL;

     UpdateData(TRUE);

     //打开记录集

     strSQL="select * fromSetCountry";

     if(!m_recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL))

     {

         MessageBox("打开数据库失败!","数据库错误",MB_OK);

         return ;

     }   

     //添加用户名到用户列表中

     int i=0;

     while(!m_recordset.IsEOF())

     {

         m_ctrListName.InsertItem(i++,m_recordset.m_name);  //往列表中添加信息InsertItem插入行

         m_recordset.MoveNext();

     }

     m_recordset.Close();   //关闭数据库

     m_ctrListName.SetRedraw(TRUE);   //重绘

}

先要创建上面的列表项标记栏目,通过InsertColumn()函数来插入一列,其中第一个参数表示第几列,如果要往后面追加列,则将第一个参数+1,用函数SetColumnWidth来设定列宽度。然后开始用InsertItem()函数插入行。

注意由于列表的插入和删除可能会造成闪屏,这里用到SetRedraw()函数FALSE表示重绘列表但是不现实出来,然后通过TRUE一次显示出来,和俄罗斯方块里面的双缓冲差不多吧。

 

4.5如何实现列表栏的点击事件

这里我们拿公司年策略设定(年假数)来举例,如下图:


首先给列表控件添加一个点击事件,双击列表控件就可添加好函数了。

然后在该函数中编写如下代码:

voidCSetCompanyDlg::OnClickListUsergroup(NMHDR* pNMHDR, LRESULT* pResult)

{

     CString strSQL;

     UpdateData(TRUE);

     //从数据库中获取选择用户名的资料

     int i = m_ctrListUser.GetSelectionMark();

     m_strPost = m_ctrListUser.GetItemText(i,0);

     strSQL.Format("select * fromSetCompany where post='%s'",m_strPost);

     if(!m_recordset.Open(AFX_DB_USE_DEFAULT_TYPE,strSQL))

     {

         MessageBox("打开数据库失败!","数据库错误",MB_OK);

         return ;

     }   

     //显示用户资料

     m_strPost = m_recordset.m_post;

     m_nOldYear = m_recordset.m_allday;

     m_nNewYear = m_recordset.m_allday;

     m_recordset.Close();  

     UpdateData(FALSE);

     *pResult = 0;

}

这里用到GetSelectionMark()函数,函数的返回值为你当前点击的行数。列表栏第一行为0。比如:你点击了“管理员”它会返回一个值为0。然后你通过GetItemText()函数来获得列表栏中这一行的文字是什么。最后将这个值传递到数据库中查询,从而显示出里点击的信息。

 

4.6数据库字段的查询

这里用撤销员工请假记录来举例,如下图:


当我们输入编号,然后点击查询,则执行数据库信息的查询,我们这里查询的原理是通过员工的ID和员工是第几次请假来进行查询,代码如下:

voidCCancelLeaveDlg::OnButtonFind()

{

     CString strSQL;

     UpdateData(TRUE);

     //从数据库中获取选择用户名的资料

     //显示用户第一次请假资料

     m_recordset.Open();

if(!m_recordset.IsEOF())

          m_recordset.MoveLast();

     while(!m_recordset.IsBOF() &&m_recordset.m_ID != m_strFind)

     {

         m_recordset.MovePrev();

     }

     if (m_recordset.IsBOF())

     {

         m_recordset.Close();

         MessageBox("未找到该员工请假记录","未找到",MB_OK);

         return;

     }

     m_strID = m_recordset.m_ID;

     m_strName = m_recordset.m_name;

     m_nTime = m_recordset.m_time;

     m_tmStart = m_recordset.m_start;

     m_tmEnd = m_recordset.m_end;

     m_strReason = m_recordset.m_reason;

     m_nDay = m_recordset.m_day;

     if (m_recordset.m_yearday)

     {

         CButton* pBtn =(CButton*)GetDlgItem(IDC_CHECK_YEAR);

         pBtn->SetCheck(1);

     }

     else

     {

         //置空年假复选框

         CButton* pBtn =(CButton*)GetDlgItem(IDC_CHECK_YEAR);

         pBtn->SetCheck(0);

     }

     m_recordset.Close();

     UpdateData(FALSE);

}

首先用Open()函数打开数据库,如果记录集不是空,则将数据库的记录集移动到最后一条MoveLast(),然后通过MovePrev()函数依次往前找,因为要撤销请假肯定是数据库里面记录的最后一次请假信息。

 

另外,也可以用SQL语句来查询,比如登陆界面账号密码匹配查询的时候,构造SQL语句如下:

strSQL.Format("select* from user where username='%s' AND password='%s'",m_strUser,m_strPass);

这句话表示从user这个表里面查询账号(username)为m_strUser,密码(password)为m_strPass的那条记录集。其中username,password为表中字段的名称。m_strUser为对话框编辑框关联的变量。最后通过GetRecordCount()函数来判断有没有找到,当GetRecordCount() == 0 的时候说明没有找到。

 

4.7数据库和MFC时间问题

这个问题纠结了我一段时间,主要在实现员工请假界面的时候,由于设定了国定假日,所以每次员工请假的时候要搜索是否在国定假日期间。

比如:10月1日-10月7日是国庆节,属于国定假日。当一个员工在9月29日(9月有30天)请假4天的时候,30- 1,2,3,4,5,6,7- 8,9,10。那么他结束日期应该在10月10号。如果在10月2号请假4天呢?10月6号请假4天呢?这些不难实现。主要在一个时间比较上面。我数据库中的时间格式是2014/01/08 21:51:12 和软件上面的时间格式是匹配的,但是我们设定国假的时候,肯定不能直接读取界面上面的时间,而要稍微处理一下,将后面的时间变成23:59:59

由于时间不可以减,我很笨,就这样实现:

     //将时间加到23小时59分59秒

     while (!(m_tmStart.GetHour() == 23&& m_tmStart.GetMinute() == 59 && m_tmStart.GetSecond() == 59))

     {

         m_tmStart += 1;

     }

     while (!(m_tmEnd.GetHour() == 23 &&m_tmEnd.GetMinute() == 59 && m_tmEnd.GetSecond() == 59))

     {

         m_tmEnd += 1;

     }

然后构造SQL语句进行查询,时间构造如下:

     //格式化时间

     strStart.Format("%4d/%02d/%02d%02d:%02d:%02d", \

         m_tmStart.GetYear(),m_tmStart.GetMonth(),m_tmStart.GetDay(),\

         m_tmStart.GetHour(),m_tmStart.GetMinute(),m_tmStart.GetSecond());

    

     strEnd.Format("%4d/%02d/%02d%02d:%02d:%02d", \

         m_tmEnd.GetYear(),m_tmEnd.GetMonth(),m_tmEnd.GetDay(),\

         m_tmEnd.GetHour(),m_tmEnd.GetMinute(),m_tmEnd.GetSecond());

     strSQL.Format("select * fromSetCountry where start >= #%s# and start <= #%s# and end >=#%s#",\

         strStart,strEnd,strEnd);

其中注意:年月日的位数。以及时间查询的格式:##双井号,不要引号。

 

4.8数据库的增加,删除,修改操作

这个很简单,值得注意的几点是:

  1. 在数据库表打开后一定要记得关闭。.Open().Close()
  2.  在执行增加删除修改之前一定要是数据库处于可编辑状态,而不只是打开。也就是要记得调用m_pSet->AddNew()或者m_pSet->Edit()。

4.9数据库的备份与恢复

voidCMainFrame::OnMenuDbad()

{

     if(AfxMessageBox("您确定要备份数据库吗?",MB_OKCANCEL)==IDCANCEL)

     {

         return;

     }  

     if(CopyFile(".\\StaffDBQ.mdb",".\\StaffDBQ.bak",FALSE))

         AfxMessageBox("数据库备份成功!");

     else

         AfxMessageBox("数据库备份失败!");

}

 

voidCMainFrame::OnMenuDbre()

{

     if(AfxMessageBox("还原数据库将覆盖原来的数据库。您确定要还原吗?",MB_OKCANCEL)==IDCANCEL)

     {

         return;

     }  

     if(CopyFile(".\\StaffDBQ.bak",".\\StaffDBQ.mdb",FALSE))

         AfxMessageBox("数据库还原成功!");

     else

         AfxMessageBox("数据库还原失败!"); 

}

 

5 测试与实现

5.1登陆界面

 


5.2程序主界面

 

 

5.3请假管理窗体

 


5.4假期设置窗体

 

5.5设定国定假日

 


5.6公司策略设定(年假数)

 


5.7删除请假记录

 


5.8按部门汇总某段时期内的请假记录

 


5.9详细列出某个员工某段时期内的所有请假记录

 


5.10撤销假期

 


其它附属界面:

a.    增加公司部门

 


b.   删除公司部门


c.   修改公司部门

 


d.   添加新员工

 

 

e.   删除员工信息

 


f.   修改员工信息

 


g.   数据库的恢复与数据库的备份



6 总结

首先总结一下编写过程中遇到的一些问题吧!

问题一:删除信息后列表框跟新不及时;

问题出现的原因:定义记录集对象为局部对象;

解决方法:将记录集对象定义在类的头文件里。

问题二:程序运行到App类的初始化函数InitInstance()中的

if(!ProcessShellCommand(cmdInfo))语句时,出现如下错误:


问题出现的原因:我开始手动增加了一个表中的一个字段,然后增加映射的时候没有指定表加错了,[already]为一个表名。

解决方法:

更改前:

     RFX_Text(pFX, _T("[ID]"), m_ID);

     RFX_Int(pFX, _T("[day]"), m_day);

更改后:

     RFX_Text(pFX,_T("[already].[ID]"), m_ID);

     RFX_Int(pFX,_T("[already].[day]"), m_day);

问题三:将程序从release版本更换到debug版本后,我跟踪运行到:

if(!pFrame->LoadFrame(m_nIDResource,

WS_OVERLAPPEDWINDOW| FWS_ADDTOTITLE,   // default framestyles

NULL,&context))

函数时出行错误。

问题出现的原因:调试里面有的断言宏出了问题

解决方法:找了好久在CSDN上面看到是对话框出现了问题,

其中这两个“√”是我手残没事干勾上去的,导致了错误!去掉就OK了。

问题四:无效游标

问题出现原因:我分析了一下,可能多个表打开关闭嵌套之中的一些问题。

解决方法:我将每个表的打开位置、关闭位置分别标记一下,挪动了几行代码的位置,解决了问题。

问题五:给列表框更换不了背景颜色

问题出现原因:单独建立一个工程,在View类响应了WM_CTLCOLOR事件,添加如下代码:

HBRUSH CSetCountryDlg::OnCtlColor(CDC* pDC, CWnd*pWnd, UINT nCtlColor)

{

HBRUSH hbr =CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

if (nCtlColor== IDC_LIST1)

{

pDC->SetBkMode(TRANSPARENT);

returnm_hbrush;

}

return hbr;

}

列表背景颜色改变成功了,但是在我的程序里面却改变不了,连if()语句都没有进来。可能是消息被拦截了。

解决方法:至今未解决。

分析程序的优缺点:

优点就是功能全部实现了,可能这也是唯一的优点了,因为自己的代码基本上就是来自Library,没什么创新和值得一提的地方,尤其是VC60的界面实在是不敢恭维。本来是添加了一个皮肤的后来感觉皮肤不好看又删掉了,就用原版的好了。

说一下,这次VC程程设计的感受吧,首先值得高兴的是,ACCESS数据库的操作基本上是熟练了,回去有时间的时候帮社联写一个答题抽奖系统,本来去年就问我让我写了,我捣鼓了好久,就是不会用数据库。然后就是感觉,课程设计真的搞的很累。基本上每天早上9:00就去18#然后就和他(她)们一起讨论,一直到晚上9:00才回宿舍,可以说是早出晚归了。总之,VC课程就这么愉快结束了,很感谢贾老师带我们的这学期,我学到很多!


源代码下载网盘:链接: http://pan.baidu.com/s/1dDDYS2d 密码: xq1b   (包括 代码+数据库+报告+流程图+模块图)


转载请保留原文地址:http://blog.csdn.net/u013055228/article/details/18056169

  • 13
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
对于学生请假系统的实现,你可以使用++来完成。下面是一个简单的示例代码,用于演示学生请假系统的基本功能: ```cpp #include <iostream> #include <string> #include <vector> using namespace std; // 学生类 class Student { private: string name; int id; public: Student(string name, int id) : name(name), id(id) {} string getName() const { return name; } int getId() const { return id; } }; // 请假记录类 class LeaveRecord { private: Student student; string reason; public: LeaveRecord(Student student, string reason) : student(student), reason(reason) {} Student getStudent() const { return student; } string getReason() const { return reason; } }; // 请假系统类 class LeaveSystem { private: vector<LeaveRecord> leaveRecords; public: void addLeaveRecord(LeaveRecord record) { leaveRecords.push_back(record); cout << "请假记录添加成功!" << endl; } void displayLeaveRecords() const { if (leaveRecords.empty()) { cout << "暂无请假记录" << endl; } else { for (const auto &record : leaveRecords) { cout << "学生姓名:" << record.getStudent().getName() << endl; cout << "学生学号:" << record.getStudent().getId() << endl; cout << "请假原因:" << record.getReason() << endl; cout << "-----------------------" << endl; } } } }; int main() { LeaveSystem leaveSystem; // 添加请假记录 Student student1("张三", 1001); LeaveRecord record1(student1, "身体不适"); leaveSystem.addLeaveRecord(record1); Student student2("李四", 1002); LeaveRecord record2(student2, "家庭原因"); leaveSystem.addLeaveRecord(record2); // 显示请假记录 leaveSystem.displayLeaveRecords(); return 0; } ``` 这段代码实现了一个简单的学生请假系统,包括学生类、请假记录类和请假系统类。你可以根据实际需求进行扩展和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值