c# winform 实践项目---人员管理系统

根据讲师下发的实践项目需求,做了一个人员管理系统,其中用到知识颇多,思来想去决定发布上来,一是为自己以后的开发做一个参考,避免忘记。二是希望能得到互联网上各位大佬的评鉴。

xx人员管理系统

xx人员管理系统是为员工提供从入职到离职全面管理的系统软件。

主要功能:追踪员工工作时数并按项目、客户或任务进行排序,支持添加员工,查询员工,显示所有员工,修改员工信息以及删除员工;提供智能化人力统计报表与分析,轻松访问统计数据,记录并分析工时表并检查每位员工的出勤情况,提供实时的数据;根据收录的信息自动生成员工简历,全面覆盖员工个人信息、合同信息、薪酬福利信息、考勤班表信息、绩效考核信息、培训经历等各类信息,为领导决策提供客观及时的数据支持。

开发目的:通过信息化减轻HR日常事务工作,提升企业整体人事运作效率。

面向领域 / 行业:人员管理

页面:
主要包含7个功能页面和一个主页面以及一个登录界面

登录
普通界面
人事部门页面
考勤管理
人员管理
信息添加
考勤查询
人员福利
普通界面
人员查询

普通员工登录与签到签出
人事部门员工的员工管理

数据库设计

*****员工表Staffs
员工编号StaffId(主) 员工编码Number 姓名Name 性别Sex 年龄Age 政治面貌Political 身高Height 体重Weight 毕业院校School 毕业专业Specialty 毕业日期GraDate 通信地址Address 联系电话Phone 招聘来源Source 相片Photo 职位编码PostNum(外) 其他Other 是否启用Enable 是否同步考勤Sync 考勤机编号MachineId(外) 录入日期EntDate 是否转正Positive
*****员工账号表StaffAccounts
账号编号AccountId(主) 员工编号StaffId(外) 账号Account 密码Password
*****考勤表Attendances
考勤编号AttendanceId(主) 员工编号StaffId(外) 签到时间SignTime 是否正常上班Work 签出时间OutTime 是否正常下班OffWork 工作时间WorkTime 签到状态SignStatus
*****职位表Posts
职位编号PostId 职位编码PostNum(主) 部门Department 职位Post
*****考勤机表AttMachines
考勤机编号MachineId(主) 考勤机名称MachineName 考勤机人员编码MStaffID 考勤人员名称MStaffName
*****公示表Formulas
公式编号FormulaId(主) 职位编码PostNum(外) 工资项名称SalaryItem 公式Formula

我设计了六个表
员工表包含所有员工基本信息及所对应的职位编码,是否是启用状态,是否同步在考勤机上,以及考勤机编号和是否已经转正
员工账号表和员工表一对一关系,保存着每个员工的账号密码
考勤表包含每个员工每天的工作状态签到签出及是否正常上班
职位表保存着公司现有的部门职位信息,根据员工职位编码进行配对职位
考勤机表包含所有考勤机和考勤机所有人员
公示表包含公司福利信息,以供计算薪资
关系
在这里插入图片描述
账号表,职位表,考勤表和考勤机表通过对应主从关系围绕着员工表,公式表仅用于计算薪资

每个表插入多个信息以供测验

页面及代码设计

首先创建DBHelper类以用于连接数据库

using System.Data.SqlClient;
using System.Data;
        //定义连接字符串
        public static string connstr = "server=.;database=PersonnelDB;uid=sa;pwd=123456";
        //声明数据库连接对象
        public static SqlConnection conn = null;
        public static void Initialization()//初始化连接
        {
            if (conn == null)//判断是否为空
            {
                //赋值连接对象
                conn = new SqlConnection(connstr);
            }
            if (conn.State == ConnectionState.Closed)//判断是否关闭
            {
                //打开连接
                conn.Open();
            }
            if (conn.State == ConnectionState.Broken)//判断是否中断
            {
                //重启连接
                conn.Close();
                conn.Open();
            }
        }
        /// <summary>
        /// 查询获取datareader
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static SqlDataReader GetDataReader(string sql)
        {
            try
            {
                Initialization();
                //声明操作对象
                SqlCommand com = new SqlCommand(sql, conn);
                //返回并定义连接关闭
                return com.ExecuteReader(CommandBehavior.CloseConnection);
            }
            catch (Exception)
            {

                throw;
            }
            
        }
        /// <summary>
        /// 断开查询
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static DataTable GetDataTable(string sql)
        {
            try
            {
                Initialization();
                //声明datatable
                DataTable dt = new DataTable();
                //声明断开式连接对象
                SqlDataAdapter da = new SqlDataAdapter(sql, conn);
                //赋值给datatable
                da.Fill(dt);
                //关闭连接
                conn.Close();
                //返回
                return dt;
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 增删改
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static bool GetExecuteNonQuery(string sql)
        {
            try
            {
                Initialization();
                //声明连接对象
                SqlCommand com = new SqlCommand(sql, conn);
                //接收返回值
                int temp = com.ExecuteNonQuery();
                //关闭连接
                conn.Close();
                //返回判断
                return temp > 0;
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 聚合函数
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static Object GetExecuteScalar(string sql)
        {
            try
            {
                Initialization();
                //声明连接对象
                SqlCommand com = new SqlCommand(sql, conn);
                //接收返回值
                Object temp = com.ExecuteScalar();
                //关闭连接
                conn.Close();
                //返回
                return temp;
            }
            catch (Exception)
            {

                throw;
            }
        }

然后为了方便整理sql语句,我就单独创建了一个dal类,用于存放SQL语句

首先是登录界面
在这里插入图片描述
我使用的是无边框窗口,设定了窗口初始显示位置
页面阴影和可拖动参考我的另一篇文章c#页面初步美化

这个界面主要就是非空验证和判断账号密码是否正确
非空验证就不多说了
判断账号密码是否正确就需要查询数据库
因此首先添加sql语句

        /// <summary>
        /// 根据账号密码查询用户信息
        /// </summary>
        /// <param name="acc"></param>
        /// <param name="pass"></param>
        /// <returns></returns>
        public static DataTable SelectByAcc(string acc,string pass)
        {
            string sql = $"SELECT * from Staffs s,StaffAccounts a,Posts p WHERE s.StaffId = a.StaffId and s.PostNum = p.PostNum and a.Account = '{acc}' and a.Password = '{pass}'";
            return DBHelper.GetDataTable(sql);
        }

并且建立user用户类,用以保存登录上的用户信息
代码如下

			DataTable user = DAL.SelectByAcc(textBox1.Text, textBox2.Text);
            if (user.Rows.Count>0)
            {
                MessageBox.Show("登陆成功!","登录成功!",MessageBoxButtons.OK);
                User.id = Convert.ToInt32(user.Rows[0]["StaffId"]);
                User.name = user.Rows[0]["Name"].ToString();
                User.photo = user.Rows[0]["Photo"].ToString();
                User.post = user.Rows[0]["Post"].ToString();
                if (user.Rows[0]["Department"].ToString().Equals("人事部"))
                {
                    //人事部门
                    Home home = new Home(this);
                    home.Show();
                    this.Hide();
                }
                else
                {
                    //其他部门
                    Staff staff = new Staff(this);
                    staff.Show();
                    this.Hide();
                }
            }
            else
            {
                MessageBox.Show("账号或密码错误!", "登录失败!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
            }

再有一个就是密码输入栏的小眼睛

            //判断是否是默认字符显示
            if (textBox2.UseSystemPasswordChar)
            {
                textBox2.UseSystemPasswordChar = false;
            }
            else
            {
                textBox2.UseSystemPasswordChar = true;
            }

登录界面没什么东西
然后下一个主页面
在这里插入图片描述
首先就是七个功能页面的跳转

            Attendance attendance = new Attendance(this);
            attendance.Show();
            this.Hide();

退出系统按钮

            if (DialogResult.OK == MessageBox.Show("您确定要退出吗?", "提示", MessageBoxButtons.OKCancel))
            {
                Application.Exit();
            }

退回登录界面

            login.Visible = true;
            this.Close();

修改密码按钮

            UpPass upPass = new UpPass();
            upPass.Show();

及页面初始化代码

            //首先显示登录人信息
            label3.Text = User.post;
            label4.Text = User.name;

            string file = "../../images/" + User.photo;
            pictureBox1.Image = Image.FromFile(file);
            //图片圆角
            DAL.PicRounded(pictureBox1);

图片圆角参考我另一个文章c#初步美化-圆角
图片框主要是通过获取当前运行文档的运行地址+用户类的存储的图片名在转换为image进行显示

其中涉及到密码修改界面
在这里插入图片描述
包含多个验证和根据id查询用户信息以及修改密码
页面初始化

添加SQL语句

        /// <summary>
        /// 根据id获取账号密码
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static DataTable SelectByPass(int id)
        {
            string sql = "SELECT * FROM StaffAccounts WHERE StaffId = " + id;
            return DBHelper.GetDataTable(sql);
        }
        string account = null;
        string password = null;
        private void UpPass_Load(object sender, EventArgs e)
        {
            DataTable dt = DAL.SelectByPass(User.id);
            account = dt.Rows[0]["Account"].ToString();
            password = dt.Rows[0]["Password"].ToString();

            label2.Text = account;
        }

主要就是显示信息
然后就是修改密码
添加SQL语句

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="id"></param>
        /// <param name="pass"></param>
        /// <returns></returns>
        public static bool UpdateByPass(int id,string pass)
        {
            string sql = $"UPDATE StaffAccounts SET Password = {pass} WHERE StaffId = {id}";
            return DBHelper.GetExecuteNonQuery(sql);
        }
            if (DAL.UpdateByPass(User.id, pass2))
            {
                MessageBox.Show("修改成功!", "提示", MessageBoxButtons.OK);
                this.Close();
            }
            else
            {
                MessageBox.Show("修改失败!", "提示", MessageBoxButtons.OK);
            }

接下来就是主菜了
考勤管理界面
在这里插入图片描述
此界面用于管理人员的考勤匹配
首先就是菜单栏和工具栏的设置
菜单栏单纯的写文字
工具栏要添加按钮模式,同时设置为图片按钮并存,用以显示图标
在一个就是datagridview的设置,一共五项
整行选中模式,不可多选单元格,可见列自动调整大小模式为fill,不允许用户添加行以及不自动添加列(代码设置,dataGridView1.AutoGenerateColumns = false)
列名设置就不多说
由于每行首列要以复选框形式显示,所以要设置列类型为DataGridViewCheckBoxColumn
但是标题栏全选复选框笔者没有实现出来,所以就在固定位置放了一个复选框,用于全选

然后就是窗口构造方法,用于通过构造方法显示上一个界面,关闭此页面,通俗来说就是返回上一页
这里使用构造方法重载,方便测试

        Form home = null;
        public Attendance(Form f)
        {
            InitializeComponent();
            home = f;
        }

接下来就是页面初始化显示
新建刷新两个视图的方法

建立全局表,用于保存数据,方便调用

        //人员表
        DataTable sa = new DataTable();
        //考勤机表
        DataTable am = new DataTable();

SQL语句

        /// <summary>
        /// 根据姓名查询所有同步到考勤机的人员
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByAttMac(string name)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p,AttMachines m WHERE s.PostNum = p.PostNum AND s.MachineId = m.MachineId AND s.Sync = 1 AND Name LIKE '%{name}%'";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 根据姓名查询全部用户信息及账号信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static DataTable SelectBySA(string name)
        {
            string sql = $"SELECT * FROM StaffAccounts sa,Staffs s,Posts p WHERE s.StaffId = sa.StaffId AND s.PostNum = p.PostNum AND Name LIKE '%{name}%'";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 初始化加载
        /// </summary>
        /// <param name="name"></param>
        public void DGVInit(string name)
        {
            am = DAL.SelectByAttMac(name);
            //table2 = am.Rows.Count;
            dataGridView2.DataSource = am;
        }
        //刷新人员表
        public void InitStaff(string dt)
        {
            sa = DAL.SelectBySA(dt);
            DataSourceBind(sa);
        }

以及格式化显示数据

        /// <summary>
        /// 判断数据
        /// </summary>
        /// <param name="dt"></param>
        public void DataSourceBind(DataTable dt)
        {
            dataGridView1.DataSource = dt;
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                if (int.Parse(dt.Rows[i]["Sync"].ToString())==0)
                {
                    dataGridView1.Rows[i].Cells[5].Value = "未同步";
                    dataGridView1.Rows[i].Cells[5].Style.BackColor = Color.Tomato;
                }
                else
                {
                    dataGridView1.Rows[i].Cells[5].Value = "已同步";
                    dataGridView1.Rows[i].Cells[5].Style.BackColor = Color.Chartreuse;
                }
                if (int.Parse(dt.Rows[i]["Enable"].ToString())==0)
                {
                    dataGridView1.Rows[i].Cells[6].Value = "未启用";
                    dataGridView1.Rows[i].Cells[6].Style.BackColor = Color.Tomato;
                }
                else
                {
                    dataGridView1.Rows[i].Cells[6].Value = "已启用";
                    dataGridView1.Rows[i].Cells[6].Style.BackColor = Color.Chartreuse;
                }
            }
        }

初始化显示数据调用

            dataGridView1.AutoGenerateColumns = false;
            dataGridView2.AutoGenerateColumns = false;

            InitStaff("");
            DGVInit("");

首列的全选及取消全选
大概思路就是点击全选按钮遍历所有复选框并选中,再次点击全部取消。点击某一个复选框遍历全部复选框,根据选中状态查看是否有选中的及未选中的,实现全选按钮的是否选中显示
但是这种仅适用于小数量的全选,数量较多的情况下就会运行缓慢,还有一个思路就是计数实现全选按钮的是否选中显示,点击某一个复选框计数加或计数减,判断是否等于总数,但是笔者实现时存在bug,为了更好显示就未采用,但理论上是可行的

        private void checkBox1_Click(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                for (int i = 0; i < dataGridView1.Rows.Count; i++)
                {
                    dataGridView1.Rows[i].Cells[0].Value = true;
                }
            }
            else
            {
                for (int i = 0; i < dataGridView1.Rows.Count; i++)
                {
                    dataGridView1.Rows[i].Cells[0].Value = false;
                }
            }
        }

以及每个单独复选框选中

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            //列索引
            if (e.ColumnIndex == 0)
            {
                //行索引
                if (Convert.ToBoolean(dataGridView1.Rows[e.RowIndex].Cells[0].EditedFormattedValue))
                {
                    for (int i = 0; i < dataGridView1.Rows.Count; i++)
                    {
                        if (!Convert.ToBoolean(dataGridView1.Rows[i].Cells[0].EditedFormattedValue))
                        {
                            return;
                        }
                    }
                    checkBox1.Checked = true;
                }
                else if(!Convert.ToBoolean(dataGridView1.Rows[e.RowIndex].Cells[0].EditedFormattedValue))
                {
                    checkBox1.Checked = false;
                }
            }
        }

在接着说菜单栏和工具栏
菜单栏也具有二级菜单,不过其功能都是实现了和工具栏相同的功能
所以这里相同代码这里就不过多介绍,只来说说工具栏
第一个按钮高级因为需求不清楚所以未能实现
第二个人员数据初始化
其主要是将所有人员的考勤机初始化,就是清空考勤机,所有人回到初始状态
修改数据库,将所有人都改成未同步

        /// <summary>
        /// 全部人员置不同步or单独置不同步
        /// </summary>
        /// <returns></returns>
        public static bool UpdateByAttMac()
        {
            string sql = "UPDATE Staffs SET Sync = 0";
            return DBHelper.GetExecuteNonQuery(sql);
        }
            if (DAL.UpdateByAttMac())
            {
                MessageBox.Show("初始化成功!", "提示", MessageBoxButtons.OK);
                InitStaff("");
                DGVInit("");
            }
            else
            {
                MessageBox.Show("初始化失败!", "提示", MessageBoxButtons.OK);
            }

第三四个按钮均是刷新,也就是调用两个显示数据的方法就行了,这里就不过多介绍了
然后是自动映射
笔者给他的功能是将所有人员全都自动同步到默认考勤机
也是修改数据库

        /// <summary>
        /// 自动映射or手动映射
        /// </summary>
        /// <returns></returns>
        public static bool UpdateBySyncMac()
        {
            string sql = "UPDATE Staffs SET Sync = 1,MachineId = 1";
            return DBHelper.GetExecuteNonQuery(sql);
        }
            if (DAL.UpdateBySyncMac())
            {
                MessageBox.Show("自动映射成功!", "提示", MessageBoxButtons.OK);
                InitStaff("");
            }
            else
            {
                MessageBox.Show("自动映射失败!", "提示", MessageBoxButtons.OK);
            }

然后就是手动映射和删除映射,笔者给它设定的功能是将人员表选中的数据同步到考勤机和考勤表选中的数据置不同步
手动映射

        public static bool UpdateBySyncMac(int id)
        {
            string sql = "UPDATE Staffs SET Sync = 1,MachineId = 1 WHERE StaffId = "+id;
            return DBHelper.GetExecuteNonQuery(sql);
        }
            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                if (Convert.ToBoolean(dataGridView1.Rows[i].Cells[0].EditedFormattedValue))
                {
                    if (!DAL.UpdateBySyncMac(Convert.ToInt32(sa.Rows[i]["StaffId"])))
                    {
                        MessageBox.Show("映射出错!", "提示", MessageBoxButtons.OK);
                        return;
                    }
                }
            }
            MessageBox.Show("映射成功!", "提示", MessageBoxButtons.OK);
            DGVInit("");
            InitStaff("");

其原理就是循环遍历选中的复选框,然后根据全局表的id行列进行改变数据
这里之所以使用判断映射出错跳出,主要是为了避免更大的数据出错吧
删除映射

        public static bool UpdateByAttMac(int id)
        {
            string sql = "UPDATE Staffs SET Sync = 0 WHERE StaffId = " + id;
            return DBHelper.GetExecuteNonQuery(sql);
        }
            for (int i = 0; i < dataGridView2.Rows.Count; i++)
            {
                if (Convert.ToBoolean(dataGridView2.Rows[i].Cells[0].EditedFormattedValue))
                {
                    if (!DAL.UpdateByAttMac(Convert.ToInt32(am.Rows[i]["StaffId"])))
                    {
                        MessageBox.Show("删除出错!", "提示", MessageBoxButtons.OK);
                        return;
                    }
                }
            }
            MessageBox.Show("删除成功!", "提示", MessageBoxButtons.OK);
            DGVInit("");
            InitStaff("");

原理同上,操作也差不多

接下来就是后边三个控件,一个是下拉框,用来选择将要搜索的表,设定为仅能选择不能输入,在一个是输入框,用来接收条件,最后一个就是搜索按钮
这里给搜索按钮添加事件执行搜索功能
搜索原理就是调用刚刚写的初始化显示数据的方法,相信有注意到那两个方法是需要参数的,它使用的sql语句也是模糊查询,模糊查询空值就是查询全部,到这里给到值,通过模糊查询显示数据
非空验证就不多说

            if (toolStripTextBox1.Text=="人员表")
            {
                InitStaff(toolStripTextBox2.Text);
            }
            else if (toolStripTextBox1.Text == "映射表")
            {
                DGVInit(toolStripTextBox2.Text);
            }
            else
            {
                MessageBox.Show("查询条件错误!", "提示", MessageBoxButtons.OK);
            }

考勤页面主要就是这些功能
人员部门页面
在这里插入图片描述
此页面用于管理人员的部门信息,包括人员统计及管理等
首先就是页面的初始化显示
构造方法同上个页面
datagirdview的初始设置就不多说了
这里设置三个全局表,分别用于表示最近七天录入,全部,和未转正

        DataTable t7 = new DataTable();
        DataTable tall = new DataTable();
        DataTable tposi = new DataTable();

初始化数据

            //获取数据
            t7 = DAL.SelectByTime7();
            tall = DAL.SelectByPost("");
            tposi = DAL.SelectByPositive();
        /// <summary>
        /// 最近七天职位数据
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static DataTable SelectByTime7()
        {
            DateTime dt = DateTime.Today.AddDays(-7);
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND EntDate > '{dt}'";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 根据姓名查询全部职位信息--1
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByPost(string name)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Name LIKE '%{name}%'";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 查询未转正职位信息
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByPositive()
        {
            string sql = "SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Positive = 0";
            return DBHelper.GetDataTable(sql);
        }

设置上方六个部门的数据统计

            //统计人数
            count1.Text = DAL.StatsDep("财务部").ToString();
            count2.Text = DAL.StatsDep("管理部").ToString();
            count3.Text = DAL.StatsDep("客服部").ToString();
            count4.Text = DAL.StatsDep("人事部").ToString();
            count5.Text = DAL.StatsDep("设计部").ToString();
            count6.Text = DAL.StatsDep("销售部").ToString();
        /// <summary>
        /// 部门人数统计
        /// </summary>
        /// <param name="dep"></param>
        /// <returns></returns>
        public static int StatsDep(string dep)
        {
            string sql = $"SELECT COUNT(*) FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Department = '{dep}'";
            return Convert.ToInt32(DBHelper.GetExecuteScalar(sql));
        }

然后就是datagirdview的数据显示,这里有个问题,因为涉及到人员头像的一个列,需要显示人员头像
因为数据库使用的是头像名字,所以这里不能直接绑定到数据,不能直接显示,所以就需要加载的时候在获取数据单独的加载头像
建立一个头像文件夹images
头像保存方法稍后再说
所以这里我单独新建了数据绑定方法

        /// <summary>
        /// 绑定数据
        /// </summary>
        /// <param name="dt"></param>
        public void DataSourceBind(DataTable dt)
        {
            dataGridView1.DataSource = dt;
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                string file = "../../images/" + dt.Rows[i]["Photo"].ToString();
                Image img = Image.FromFile(file);
                Image imgb = new Bitmap(img);
                dataGridView1.Rows[i].Cells[0].Value = imgb;
                img.Dispose();
            }
        }

先绑定,然后遍历传过来的表获取地址挨个绑定,这里因为是图片流的方式,所以这里会造成内存溢出,这就涉及到图片加载的问题了,这就造成在低性能机子上会报错,笔者因为实力有限这个问题未能解决。
接下来是单击统计数值显示这个部门的信息

            DataSourceBind(DAL.SelectByDepPost("财务部"));

            txt1.ForeColor = Color.Black;
            txt2.ForeColor = Color.Red;
            txt3.ForeColor = Color.Black;
        /// <summary>
        /// 根据部门查询职位信息---2
        /// </summary>
        /// <param name="dep"></param>
        /// <returns></returns>
        public static DataTable SelectByDepPost(string dep)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Department LIKE '%{dep}%'";
            return DBHelper.GetDataTable(sql);
        }

还有就是单击全部,最近和未转正标签切换对应显示数据的功能

            DataSourceBind(t7);

            txt1.ForeColor = Color.Red;
            txt2.ForeColor = Color.Black;
            txt3.ForeColor = Color.Black;

需要什么直接传表就行
然后就是工具栏搜索功能

            if (toolStripTextBox1.Text != "")
            {
                DataSourceBind(DAL.SelectByPost(toolStripTextBox1.Text));
            }
            else
            {
                DataSourceBind(tall);
            }
            txt1.ForeColor = Color.Black;
            txt2.ForeColor = Color.Red;
            txt3.ForeColor = Color.Black;

根据名字模糊搜索

        /// <summary>
        /// 根据姓名查询全部职位信息--1
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByPost(string name)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Name LIKE '%{name}%'";
            return DBHelper.GetDataTable(sql);
        }

如果输入框为空就直接显示全部数据
单击新建档案就直接跳转到第三个功能界面

            AddStaff addStaff = new AddStaff(this,1);
            addStaff.Show();
            this.Hide();

人员添加界面
在这里插入图片描述
此页面主要用于添加人员信息
因为有多个界面需要跳转到这个界面,所以这里出了单纯传页面有多个构造方法的重载

        public AddStaff(Form f,int s)
        {
            InitializeComponent();
            home = f;
            button2.Text = "退回上页";
        }

这个主要是设置返回主页的按钮显示

        int Id = -1;
        public AddStaff(Form f, int s,int id)
        {
            InitializeComponent();
            home = f;
            button2.Text = "退回上页";
            Id = id;

            if (s==0)
            {
                button7.Enabled = false;
            }
            else
            {
                button6.Enabled = false;
                button7.Enabled = false;
            }
        }

这个创建了一个全局int值,用于保存id值,接受这个id值用于显示数据,以及保存和重置按钮的是否可用
然后就是初始化显示
首先获取部门信息添加到下拉框

            DataTable dt = DAL.SelectByDepartment();
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                comboBox2.Items.Add(dt.Rows[i][0]);
            }
        /// <summary>
        /// 查询所有不重复部门
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByDepartment()
        {
            string sql = "SELECT DISTINCT Department FROM Posts";
            return DBHelper.GetDataTable(sql);
        }

然后因为这个界面有富文本编辑框,有个字体设置

            //添加系统字体
            foreach (FontFamily f in FontFamily.Families)
            {
                toolStripComboBox1.Items.Add(f.Name);
            }

还有就是当id有值的时候,也就是不等于初始值的时候,就显示当前id所有的用户信息以供查看和修改

            //用户编辑
            if (Id != -1)
            {
            //内容省略,主要就是给每个输入框赋值
            }

这里说下用户头像的保存方式
点击选择头像按钮

        //头像路径
        string imagefile = "";
        private void button5_Click(object sender, EventArgs e)
        {
            if (DialogResult.OK == openFileDialog1.ShowDialog())
            {
                imagefile = openFileDialog1.FileName;
                pictureBox1.ImageLocation = openFileDialog1.FileName;
            }
        }

这里保存下来当先头像的物理路径,以供保存,同时显示图片预选
当点击了保存之后再进行保存操作
然后就是富文本框的设置
第一个字体
就是赋值一个新的字体

        //字体
        private void toolStripComboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            richTextBox1.Font = new Font(FontFamily.Families[toolStripComboBox1.SelectedIndex], richTextBox1.Font.Size, richTextBox1.Font.Style);
        }

加粗

        //富文本应用
        Font oldFont;
        Font newFont;
        //加粗
        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            oldFont = richTextBox1.SelectionFont;
            if (oldFont.Bold)
            {
                newFont = new Font(oldFont, oldFont.Style & ~FontStyle.Bold);//交集
            }
            else
            {
                newFont = new Font(oldFont, oldFont.Style | FontStyle.Bold);//并集
            }
            richTextBox1.SelectionFont = newFont;
        }

这里要解释下,判断是否是加粗状态,如果是则取当前字体样式&除了加粗字体(即倾斜下划线)的并集,取消加粗,并保留原来的字体样式。如果否则取当前字体样式和加粗的并集,即加粗
倾斜

        //倾斜
        private void toolStripButton3_Click(object sender, EventArgs e)
        {
            oldFont = richTextBox1.SelectionFont;
            if (oldFont.Italic)
            {
                newFont = new Font(oldFont, oldFont.Style & ~FontStyle.Italic);//交集
            }
            else
            {
                newFont = new Font(oldFont, oldFont.Style | FontStyle.Italic);//并集
            }
            richTextBox1.SelectionFont = newFont;
        }

下划线

        //下划线
        private void toolStripButton4_Click(object sender, EventArgs e)
        {
            oldFont = richTextBox1.SelectionFont;
            if (oldFont.Underline)
            {
                newFont = new Font(oldFont, oldFont.Style & ~FontStyle.Underline);//交集
            }
            else
            {
                newFont = new Font(oldFont, oldFont.Style | FontStyle.Underline);//并集
            }
            richTextBox1.SelectionFont = newFont;
        }

倾斜和下划线原理相同
字号加减

        //字号加
        private void toolStripButton5_Click(object sender, EventArgs e)
        {
            richTextBox1.Font = new Font(richTextBox1.Font.FontFamily,richTextBox1.Font.Size + 1, richTextBox1.Font.Style);
        }
        //字号减
        private void toolStripButton6_Click(object sender, EventArgs e)
        {
            richTextBox1.Font = new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size - 1, richTextBox1.Font.Style);
        }

前后景色

        //前景色/字体
        private void toolStripButton7_Click(object sender, EventArgs e)
        {
            if (DialogResult.OK == colorDialog1.ShowDialog())
            {
                richTextBox1.ForeColor = colorDialog1.Color;
            }
        }
        //背景色
        private void toolStripButton8_Click(object sender, EventArgs e)
        {
            if (DialogResult.OK == colorDialog1.ShowDialog())
            {
                richTextBox1.BackColor = colorDialog1.Color;
            }
        }

左中右对齐

        //左中右对其
        private void toolStripButton9_Click(object sender, EventArgs e)
        {
            richTextBox1.SelectionAlignment = HorizontalAlignment.Left;
        }

        private void toolStripButton10_Click(object sender, EventArgs e)
        {
            richTextBox1.SelectionAlignment = HorizontalAlignment.Center;
        }

        private void toolStripButton11_Click(object sender, EventArgs e)
        {
            richTextBox1.SelectionAlignment = HorizontalAlignment.Right;
        }

添加个链接

        //连接
        private void toolStripButton14_Click(object sender, EventArgs e)
        {
            richTextBox1.SelectedText = "http://";
        }

有序和无序添加

        //有序
        private void toolStripButton12_Click(object sender, EventArgs e)
        {
            string[] strs = richTextBox1.SelectedText.Split(new char[2] {'\n', '\r'});
            string txt = "";
            for (int i = 0; i < strs.Length; i++)
            {
                txt += (i + 1) + "." + strs[i] + "\n";
            }
            richTextBox1.SelectedText = txt;
        }
        //无序
        private void toolStripButton13_Click(object sender, EventArgs e)
        {
            string[] strs = richTextBox1.SelectedText.Split(new char[2] { '\n', '\r' });
            string txt = "";
            for (int i = 0; i < strs.Length; i++)
            {
                txt += "*." + strs[i] + "\n";
            }
            richTextBox1.SelectedText = txt;
        }

这里因为时间问题仅实现了添加没有实现取消,但其实原理相同,获取当前选中的数据,并根据回车符分成string数组,遍历在每个string的前面添加符号就行了。
取消的话原理也类似,只是判断下是否有符号,有的话切掉就行了
最后在添加到所选位置

然后是重置按钮

        //重置
        private void button7_Click(object sender, EventArgs e)
        {
            Init();
        }
        /// <summary>
        /// 重置
        /// </summary>
        public void Init()
        {
            foreach (Control c in this.panel1.Controls)
            {
                if (c is TextBox)
                {
                    ((TextBox)c).Text = "";
                }
            }
            comboBox1.Text = "";
            comboBox2.Text = "";
            comboBox3.Text = "";

            richTextBox1.Text = "";

            pictureBox1.Image = null;
        }

这里新建了个方法,方便保存数据后重置
遍历所有控件,如果为输入框就清空
以及下拉框和富文本以及图片框清空
接下来就是保存
非空验证就不说了
这里我还加了个进度条显示保存进度

            //保存图片
            progressBar1.Value = 0;
            string name = DateTime.Now.Ticks.ToString();
            string enttime = DateTime.Now.ToString();
            progressBar1.Value = 10;
            if (imagefile != "")
            {
                File.Copy(imagefile, "../../images/" + name + ".jpg");
            }
            progressBar1.Value = 20;

以当前时间为基础取周期值为图片名称,防止图片重复,然后复制过图片来
乱入的enttime用于保存保存时间的

string imagename = name + ".jpg";
            progressBar1.Value = 30;
            string postID = "";
            switch (comboBox2.Text)
            {
                case "财务部":
                    postID += "C";
                    break;
                case "管理部":
                    postID += "G";
                    break;
                case "客服部":
                    postID += "K";
                    break;
                case "人事部":
                    postID += "R";
                    break;
                case "销售部":
                    postID += "X";
                    break;
                case "设计部":
                    postID += "S";
                    break;
                default:
                    break;
            }
            progressBar1.Value = 40;
            switch (comboBox3.Text)
            {
                case "主管":
                    postID += "zg";
                    break;
                case "助理":
                    postID += "zl";
                    break;
                case "员工":
                    postID += "yg";
                    break;
                default:
                    break;
            }
            progressBar1.Value = 50;
            string source = "";
            if (radioButton3.Checked)
            {
                source = "招聘会";
            }
            else if (radioButton4.Checked)
            {
                source = "智联招聘";
            }
            else if (radioButton5.Checked)
            {
                source = "前程无忧";
            }
            else if (radioButton6.Checked)
            {
                source = "中华英才";
            }
            else if (radioButton7.Checked)
            {
                source = "其他";
            }

保存信息
然后中间穿插了修改信息,也就是id不为初始值时

            //修改
            if (Id != -1)
            {
                progressBar1.Value = 60;
                if (!DAL.UpdateStaff(textBox1.Text, radioButton1.Checked ? "男" : "女", int.Parse(textBox7.Text), comboBox1.Text, int.Parse(textBox4.Text), int.Parse(textBox8.Text), textBox2.Text, textBox5.Text, textBox9.Text + " " + textBox10.Text + " " + textBox11.Text, textBox3.Text, textBox6.Text, source, imagename, richTextBox1.Text, postID,Id))
                {
                    MessageBox.Show("保存出错!", "提示:用户信息出错", MessageBoxButtons.OK);
                    return;
                }
                //删除原来的图片
                if (imagefile != "")
                {
                    File.Delete(oldimage);
                }
                progressBar1.Value = 80;
                if (!DAL.UpdateStaffAcc(textBox13.Text, textBox12.Text,Id))
                {
                    MessageBox.Show("保存出错!", "提示:账号信息出错", MessageBoxButtons.OK);
                    return;
                }
                progressBar1.Value = 100;
                MessageBox.Show("保存成功!", "提示", MessageBoxButtons.OK);
                progressBar1.Value = 0;
                Init();
                return;
            }
        //修改用户
        public static bool UpdateStaff(string Name,string Sex, int Age, string Political, int Height, int Weight, string School, string Specialty, string GraDate, string Address, string Phone, string Source, string Photo, string Other,string PostNum, int id)
        {
            string sql = $"UPDATE Staffs SET Name = '{Name}',Sex = '{Sex}',Age = {Age},Political = '{Political}',Height = {Height},Weight = {Weight},School = '{School}',Specialty = '{Specialty}',GraDate = '{GraDate}',Address = '{Address}',Phone = '{Phone}',Source = '{Source}',Photo = '{Photo}',Other = '{Other}',PostNum = '{PostNum}' WHERE StaffId = {id}";
            return DBHelper.GetExecuteNonQuery(sql);
        }
        /// <summary>
        /// 修改账号
        /// </summary>
        /// <param name="Account"></param>
        /// <param name="Password"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool UpdateStaffAcc(string Account,string Password,int id)
        {
            string sql = $"UPDATE StaffAccounts SET Account = '{Account}',Password = '{Password}' WHERE StaffId = {id}";
            return DBHelper.GetExecuteNonQuery(sql);
        }

用以修改信息,以及删除原来保存的头像和修改账号
如果是修改信息的话到这一步就return返回了
最后就是保存信息

            progressBar1.Value = 60;
            if (!DAL.InsertStaff(name,textBox1.Text,radioButton1.Checked?"男":"女",int.Parse(textBox7.Text),comboBox1.Text, int.Parse(textBox4.Text), int.Parse(textBox8.Text),textBox2.Text,textBox5.Text,textBox9.Text+" "+textBox10.Text+" "+textBox11.Text,textBox3.Text,textBox6.Text,source,imagename,postID,richTextBox1.Text,enttime))
            {
                MessageBox.Show("保存出错!","提示:用户信息出错",MessageBoxButtons.OK);
                return;
            }
            progressBar1.Value = 80;
            //查询id
            int id = DAL.SelectByNumber(name);
            if (!DAL.InsertStaffAcc(id,textBox13.Text,textBox12.Text))
            {
                MessageBox.Show("保存出错!", "提示:账号信息出错", MessageBoxButtons.OK);
                return;
            }
            progressBar1.Value = 100;
            MessageBox.Show("保存成功!", "提示",MessageBoxButtons.OK);
            progressBar1.Value = 0;
            Init();

首先是保存人员信息

        //添加用户
        public static bool InsertStaff(string Number,string Name,string Sex,int Age,string Political,int Height,int Weight,string School,string Specialty,string GraDate,string Address,string Phone,string Source,string Photo,string PostNum,string Other,string EntDate)
        {
            string sql = $"INSERT INTO Staffs(Number,Name,Sex,Age,Political,Height,Weight,School,Specialty,GraDate,Address,Phone,Source,Photo,PostNum,Other,Enable,Sync,MachineId,EntDate,Positive) VALUES('{Number}','{Name}','{Sex}',{Age},'{Political}',{Height},{Weight},'{School}','{Specialty}','{GraDate}','{Address}','{Phone}','{Source}','{Photo}','{PostNum}','{Other}',1,0,1,'{EntDate}',0)";
            return DBHelper.GetExecuteNonQuery(sql);
        }

然后根据用户编码查询id

        /// <summary>
        /// 根据编号查询id
        /// </summary>
        /// <param name="num"></param>
        /// <returns></returns>
        public static int SelectByNumber(string num)
        {
            string sql = $"SELECT StaffId FROM Staffs WHERE Number = '{num}'";
            return Convert.ToInt32(DBHelper.GetDataTable(sql).Rows[0][0]);
        }

最后保存账号

        /// <summary>
        /// 添加账号
        /// </summary>
        /// <param name="id"></param>
        /// <param name="acc"></param>
        /// <param name="pass"></param>
        /// <returns></returns>
        public static bool InsertStaffAcc(int id,string acc,string pass)
        {
            string sql = $"INSERT INTO StaffAccounts(StaffId,Account,Password) VALUES({id},'{acc}','{pass}')";
            return DBHelper.GetExecuteNonQuery(sql);
        }

大概功能就这些
人员考勤界面
在这里插入图片描述
此页面就是显示数据,和界面的排序
页面初始化

        DataTable dt = new DataTable();
			dt = DAL.SelectByPost("");
            dt.Columns.Add("countWork");
            dt.Columns.Add("countOffWork");
            dt.Columns.Add("countLate");
            dt.Columns.Add("countLeave");
            dt.Columns.Add("countClock");
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                DataRow dr = DAL.SelectCountAtt(Convert.ToInt32(dt.Rows[i]["StaffId"]));
                dt.Rows[i]["countWork"] = dr[0];
                dt.Rows[i]["countOffWork"] = dr[1];
                dt.Rows[i]["countLate"] = dr[2];
                dt.Rows[i]["countLeave"] = dr[3];
                dt.Rows[i]["countClock"] = dr[4];
            }

            DisplayData(dt);

添加五列,分别用于记录正常上下班迟到早退和正常上班次数
再遍历数据表给每个数据赋值

        /// <summary>
        /// 根据姓名查询全部职位信息--1
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByPost(string name)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Name LIKE '%{name}%'";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 统计考勤次数
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static DataRow SelectCountAtt(int id)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add();
            dt.Columns.Add();
            dt.Columns.Add();
            dt.Columns.Add();
            dt.Columns.Add();
            DataRow dr = dt.NewRow();
            string sql1 = "SELECT COUNT(*) FROM Staffs s,Attendances a WHERE s.StaffId = a.StaffId AND s.StaffId = "+id;
            int count = Convert.ToInt32(DBHelper.GetDataTable(sql1).Rows[0][0]);
            string sql2 = "SELECT COUNT(*) FROM Staffs s,Attendances a WHERE s.StaffId = a.StaffId AND a.[Work] = 1 AND s.StaffId = "+id;
            int work = Convert.ToInt32(DBHelper.GetDataTable(sql2).Rows[0][0]);
            string sql3 = "SELECT COUNT(*) FROM Staffs s,Attendances a WHERE s.StaffId = a.StaffId AND a.OffWork = 1 AND s.StaffId = " + id;
            int offwork = Convert.ToInt32(DBHelper.GetDataTable(sql3).Rows[0][0]);
            string sql4 = "SELECT COUNT(*) FROM Staffs s,Attendances a WHERE s.StaffId = a.StaffId AND a.SignStatus = 1 AND s.StaffId = " + id;
            int SignStatus = Convert.ToInt32(DBHelper.GetDataTable(sql4).Rows[0][0]);
            dr[0] = work;
            dr[1] = offwork;
            dr[2] = count - work;
            dr[3] = count - offwork;
            dr[4] = count - SignStatus;
            return dr;
        }

分别查找次数并返回

这里涉及到一个分页
讲一下分页类,我单独创建了分页类,用于对页面进行分类,并且可用于多界面
分页
界面
在这里插入图片描述
分页类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;

namespace PersonnelSySt
{
    public class Pagination
    {
        public DataTable dt = new DataTable();//数据
        public int rowcount;//表行数
        public int pagerowcount;//每页行数
        public int pagecount;//页数
        public int lastrowcount;//最后页行数
        public Pagination()
        {
            
        }
        /// <summary>
        /// 数据初始化
        /// </summary>
        /// <param name="t"></param>
        /// <param name="prc"></param>
        public void Init(DataTable t, int prc)
        {
            dt = t;
            rowcount = dt.Rows.Count;
            pagerowcount = prc;
            pagecount = (rowcount / pagerowcount)+1;
            lastrowcount = rowcount % pagerowcount;
        }
        /// <summary>
        /// 获取当前页数据
        /// </summary>
        /// <param name="page"></param>
        /// <returns></returns>
        public DataTable GetData(int page)
        {
            int up = (page - 1) * pagerowcount;
            if (up>rowcount)
            {
                return new DataTable();
            }
            int last = 0;
            if (page == pagecount)
            {
                last = up + lastrowcount;
            }
            else
            {
                last = up + pagerowcount;
            }
            //复制表数据
            DataTable newdt = dt.Copy();
            //清除行
            newdt.Rows.Clear();
            for (int i = up; i < last; i++)
            {
            	//将行添加到表
                newdt.ImportRow(dt.Rows[i]);
            }
            return newdt;
        }
        /// <summary>
        /// 获取页数
        /// </summary>
        /// <returns></returns>
        public int GetPageCount()
        {
            return pagecount;
        }
        /// <summary>
        /// 修改每页行数
        /// </summary>
        /// <param name="pc"></param>
        public void UpdatePageCount(int prc)
        {
            pagerowcount = prc;
            pagecount = (rowcount / pagerowcount) + 1;
            lastrowcount = rowcount % pagerowcount;
        }
    }
}

主要是根据初始化方法传入初始值,包括表和每页行数
又根据传入的值计算行数页数及最后页行数
GetData方法是根据接受的页码返回对应的分段数据
再加上获取页数和修改每页行数代码完善分页类
创建全局分页

        Pagination p = new Pagination();

再回到界面,需要分类就要单独创建一个数据绑定方法用于分段显示

        //分页
        public void DisplayData(DataTable newdt)
        {
            p.Init(newdt, 10);
            dataGridView1.DataSource = p.GetData(1);
            btnEna(1);
        }

因为涉及到上一页下一页的按钮是否可用,所有又创建了一个根据页数显示页面的方法,用于设置上一页下一页的是否可用和当前页值的显示

        public void btnEna(int page)
        {
            txtpage.Text = page.ToString();
            txtJump.Text = page.ToString();
            int count = p.GetPageCount();
            if (page == 1)
            {
                Previous.Enabled = false;
            }
            else
            {
                Previous.Enabled = true;
            }
            if (page == count)
            {
                Next.Enabled = false;
            }
            else
            {
                Next.Enabled = true;
            }
        }

单击上一页

            int page = int.Parse(txtpage.Text) - 1;
            dataGridView1.DataSource = p.GetData(page);
            btnEna(page);

下一页

            int page = int.Parse(txtpage.Text) + 1;
            dataGridView1.DataSource = p.GetData(page);
            btnEna(page);

首页

            dataGridView1.DataSource = p.GetData(1);
            btnEna(1);

尾页

            dataGridView1.DataSource = p.GetData(p.GetPageCount());
            btnEna(p.GetPageCount());

设置每页页数

            p.UpdatePageCount(int.Parse(txtpagecount.Text));
            dataGridView1.DataSource = p.GetData(1);
            btnEna(1);

跳转到某页

            int n = int.Parse(txtJump.Text);
            if (n > 0 && n <= p.GetPageCount())
            {
                dataGridView1.DataSource = p.GetData(n);
                btnEna(n);
            }

这就是分页的全部内容
然后是工具栏的各种排序,这个思路简单,设置datagirdview的列排序就行
上班排序

        //上班排序
        bool work = true;
        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if (work)
            {
                dataGridView1.Sort(dataGridView1.Columns[3], ListSortDirection.Ascending);
                work = false;
            }
            else
            {
                dataGridView1.Sort(dataGridView1.Columns[3], ListSortDirection.Descending);
                work = true;
            }
        }

下班排序

        //下班排序
        bool offwork = true;
        private void 正常下班次数排序ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (offwork)
            {
                dataGridView1.Sort(dataGridView1.Columns[4], ListSortDirection.Ascending);
                offwork = false;
            }
            else
            {
                dataGridView1.Sort(dataGridView1.Columns[4], ListSortDirection.Descending);
                offwork = true;
            }
        }

迟到排序

        //迟到排序
        bool late = true;
        private void 迟到次数排序ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (late)
            {
                dataGridView1.Sort(dataGridView1.Columns[5], ListSortDirection.Ascending);
                late = false;
            }
            else
            {
                dataGridView1.Sort(dataGridView1.Columns[5], ListSortDirection.Descending);
                late = true;
            }
        }

早退排序

        //早退排序
        bool leave = true;
        private void 早退次数排序ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (leave)
            {
                dataGridView1.Sort(dataGridView1.Columns[6], ListSortDirection.Ascending);
                leave = false;
            }
            else
            {
                dataGridView1.Sort(dataGridView1.Columns[6], ListSortDirection.Descending);
                leave = true;
            }
        }

不正常打卡排序

        //不正常打卡排序
        bool clock = true;
        private void 不正常打卡次数排序ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (clock)
            {
                dataGridView1.Sort(dataGridView1.Columns[7], ListSortDirection.Ascending);
                clock = false;
            }
            else
            {
                dataGridView1.Sort(dataGridView1.Columns[7], ListSortDirection.Descending);
                clock = true;
            }
        }

此页功能大概就这些
人员福利界面
在这里插入图片描述
这个界面主要就是对于员工福利的查询和添加的小功能
顶多附加分页,分页功能就不说了,参考上页
初始显示

            DisplayData(DAL.SelectBySalary(""));

            DataTable dt = DAL.SelectByDepartment();
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                comboBox1.Items.Add(dt.Rows[i][0]);
            }

获取数据

        /// <summary>
        /// 根据名称查询福利
        /// </summary>
        /// <param name="sal"></param>
        /// <returns></returns>
        public static DataTable SelectBySalary(string sal)
        {
            string sql = $"SELECT Department,SalaryItem,Formula FROM Formulas f,Posts p WHERE f.PostNum = p.PostNum AND SalaryItem LIKE '%{sal}%'";
            return DBHelper.GetDataTable(sql);
        }

获取所有部门

        /// <summary>
        /// 查询所有不重复部门
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByDepartment()
        {
            string sql = "SELECT DISTINCT Department FROM Posts";
            return DBHelper.GetDataTable(sql);
        }

根据名称模糊查询

            if (textBox1.Text != "")
            {
                DisplayData(DAL.SelectBySalary(textBox1.Text));
            }
            else
            {
                DisplayData(DAL.SelectBySalary(""));
            }
        /// <summary>
        /// 根据名称查询福利
        /// </summary>
        /// <param name="sal"></param>
        /// <returns></returns>
        public static DataTable SelectBySalary(string sal)
        {
            string sql = $"SELECT Department,SalaryItem,Formula FROM Formulas f,Posts p WHERE f.PostNum = p.PostNum AND SalaryItem LIKE '%{sal}%'";
            return DBHelper.GetDataTable(sql);
        }

根据部门查询

            if (comboBox1.Text != "")
            {
                DisplayData(DAL.SelectByFormulas(comboBox1.Text));
            }
            else
            {
                DisplayData(DAL.SelectBySalary(""));
            }
        /// <summary>
        /// 根据部门查询福利
        /// </summary>
        /// <param name="dep"></param>
        /// <returns></returns>
        public static DataTable SelectByFormulas(string dep)
        {
            string sql = $"SELECT Department,SalaryItem,Formula FROM Formulas f,Posts p WHERE f.PostNum = p.PostNum AND Department LIKE '%{dep}%'";
            return DBHelper.GetDataTable(sql);
        }

还有就是添加按钮会打开添加小界面
在这里插入图片描述
点击保存

            if (comboBox1.Text == ""||textBox1.Text == ""||textBox2.Text == "")
            {
                MessageBox.Show("数据为空!","提示:",MessageBoxButtons.OK);
                return;
            }
            string dep = "";
            switch (comboBox1.Text)
            {
                case "管理部":
                    dep = "Gzg";
                    break;
                case "人事部":
                    dep = "Rzg";
                    break;
                case "财务部":
                    dep = "Czg";
                    break;
                case "客服部":
                    dep = "Kzg";
                    break;
                case "设计部":
                    dep = "Szg";
                    break;
                case "销售部":
                    dep = "Xzg";
                    break;
                default:
                    break;
            }
            if (DAL.InsertFormula(dep,textBox1.Text,textBox2.Text))
            {
                MessageBox.Show("添加成功!", "提示!", MessageBoxButtons.OK);
                this.Close();
            }
            else
            {
                MessageBox.Show("添加失败!", "提示!", MessageBoxButtons.OK);
            }

保存数据

        /// <summary>
        /// 添加公式套
        /// </summary>
        /// <param name="dep"></param>
        /// <param name="name"></param>
        /// <param name="fo"></param>
        /// <returns></returns>
        public static bool InsertFormula(string dep,string name,string fo)
        {
            string sql = $"INSERT INTO Formulas(PostNum,SalaryItem,Formula) VALUES('{dep}','{name}','{fo}')";
            return DBHelper.GetExecuteNonQuery(sql);
        }

这个页面就没功能了,就是添加查询
人员查询界面
在这里插入图片描述
这个界面有两个大功能,一个是三个表的查询并且合并到一起去重显示,第二个就是点击每行的编辑和查看按钮传递数据跳转到人员添加界面,上面设置的构造函数便用上了
分页和初始显示数据就不多说了
搜索功能的实现

            if (textBox1.Text != "")
            {
                DataTable t1 = DAL.SelectByPost(textBox1.Text);
                List<object> l = new List<object>();
                foreach (DataRow dr in t1.Rows)
                {
                    l.Add(dr["StaffId"]);
                }
                DataTable t2 = DAL.SelectByDepPost(textBox1.Text);
                DataTable t3 = DAL.SelectByNumPost(textBox1.Text);
                foreach (DataRow dr in t2.Rows)
                {
                    if (!l.Contains(dr["StaffId"]))
                    {
                        t1.ImportRow(dr);
                    }
                }
                foreach (DataRow dr in t3.Rows)
                {
                    if (!l.Contains(dr["StaffId"]))
                    {
                        t1.ImportRow(dr);
                    }
                }
                DisplayData(t1);
            }
            else
            {
                DisplayData(DAL.SelectByPost(""));
            }

三个表合并好说,主要是去重
我的思路是根据人员id去重
先查询出一个表,并且获取查询出来的所有数据的id保存到集合中
再判断是否存在于集合内以保存不重复的数据
最后显示

        /// <summary>
        /// 根据姓名查询全部职位信息--1
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByPost(string name)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Name LIKE '%{name}%'";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 根据部门查询职位信息---2
        /// </summary>
        /// <param name="dep"></param>
        /// <returns></returns>
        public static DataTable SelectByDepPost(string dep)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Department LIKE '%{dep}%'";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 根据用户编号查询用户信息---3
        /// </summary>
        /// <param name="num"></param>
        /// <returns></returns>
        public static DataTable SelectByNumPost(string num)
        {
            string sql = $"SELECT * FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum AND Number LIKE '%{num}%'";
            return DBHelper.GetDataTable(sql);
        }

然后是单击表内编辑和查看按钮
首先要将最后两列的类型设置为按钮类型哟

            if (e.ColumnIndex == 11)
            {
                int id = Convert.ToInt32(dataGridView1.Rows[e.RowIndex].Cells[0].Value);
                AddStaff addStaff = new AddStaff(this,0,id);
                addStaff.Show();
                this.Hide();
            }
            else if (e.ColumnIndex == 12)
            {
                int id = Convert.ToInt32(dataGridView1.Rows[e.RowIndex].Cells[0].Value);
                AddStaff addStaff = new AddStaff(this, 1, id);
                addStaff.Show();
                this.Hide();
            }

将id传过去,根据上边说过的构造函数执行修改和查看操作

普通人员界面
在这里插入图片描述
一共两个地方可以到此页面,就是登陆的时候如果是非人事部员工就会到此页面,还有就是主页面的跳转
这个界面主要用到一个控件tabcontrol,用于切换小组界面,功能也显而易见
主要用到七个功能,简介,签到,请假,公司基本信息,公告,留言,查看公司通讯录
简介和基本信息就是介绍的,根据自己风格兴趣写就行
下面说下其他几个
签到
显示数据和视图设置就不多说,主要就是签到和签出
签到

            DateTime n = DateTime.Now;
            //判断是否正常上班
            int b = n.Hour<8 ? 1 : 0;
            if (DAL.InsertByAtt(User.id,n,b,0))
            {
                MessageBox.Show("签到成功!","提示",MessageBoxButtons.OK);
                //刷新数据
                DGVInit();
            }
            else
            {
                MessageBox.Show("签到失败!", "提示", MessageBoxButtons.OK);
            }
        /// <summary>
        /// 签到
        /// </summary>
        /// <param name="id"></param>
        /// <param name="STime"></param>
        /// <param name="work"></param>
        /// <param name="Sign"></param>
        /// <returns></returns>
        public static bool InsertByAtt(int id, DateTime STime, int work, int Sign)
        {
            string sql = $"insert into Attendances(StaffId,SignTime,[Work],SignStatus) VALUES ({id},'{STime}',{work},{Sign})";
            return DBHelper.GetExecuteNonQuery(sql);
        }

签出
这里实现思路:一般公司正常流程签到后就是签出,所以只需寻找数据库最新的一条数据修改签出时间就行

            //获取签到信息
            DataTable dt = DAL.SelectBySign(User.id);
            int id = Convert.ToInt32(dt.Rows[0]["AttendanceId"]);
            DateTime d = Convert.ToDateTime(dt.Rows[0]["SignTime"]);
            int dn = Convert.ToInt32(dt.Rows[0]["Work"]);

            //签出时间
            DateTime n = DateTime.Now;
            //是否正常下班
            int b = n.Hour > 17 ? 1 : 0;

			//上班时间
            string time = (n - d).Hours.ToString() + "." + (n - d).Minutes.ToString() + ":" + (n - d).Milliseconds.ToString();
            if (DAL.UpdateByOut(n,b,time,dn==1&&b==1?1:0,id))
            {
                MessageBox.Show("签出成功!", "提示", MessageBoxButtons.OK);
                //刷新
                DGVInit();
            }
            else
            {
                MessageBox.Show("签出失败!", "提示", MessageBoxButtons.OK);
            }
        /// <summary>
        /// 根据id查询签到
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static DataTable SelectBySign(int id)
        {
            string sql = $"SELECT AttendanceId,SignTime,[Work] FROM Attendances WHERE StaffId = {id} ORDER BY AttendanceId DESC";
            return DBHelper.GetDataTable(sql);
        }
        /// <summary>
        /// 签出
        /// </summary>
        /// <param name="Out"></param>
        /// <param name="work"></param>
        /// <param name="time"></param>
        /// <param name="sign"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool UpdateByOut(DateTime Out,int work,string time,int sign,int id)
        {
            string sql = $"UPDATE Attendances SET OutTime = '{Out}',OffWork = {work},WorkTime = '{time}',SignStatus = {sign} WHERE AttendanceId = " + id;
            return DBHelper.GetExecuteNonQuery(sql);
        }

然后就是请假,我这里是添加了一个具有起始时间的状态为2的请假信息

            if (DialogResult.OK == MessageBox.Show("确认申请请假吗?","提示",MessageBoxButtons.OKCancel))
            {
                string D = (dateTimePicker2.Value - dateTimePicker1.Value).Days.ToString();
                string H = (dateTimePicker2.Value - dateTimePicker1.Value).Hours.ToString();
                string M = (dateTimePicker2.Value - dateTimePicker1.Value).Minutes.ToString();
                if (DAL.InsertByAtt(User.id,dateTimePicker1.Value,1,dateTimePicker2.Value,1,D+"."+H+":"+M,2))
                {
                    MessageBox.Show("申请成功!", "提示", MessageBoxButtons.OK);
                    DGVInit();
                }
                else
                {
                    MessageBox.Show("申请失败!", "提示", MessageBoxButtons.OK);
                }
            }
        /// <summary>
        /// 添加考勤信息
        /// </summary>
        /// <param name="id"></param>
        /// <param name="STime"></param>
        /// <param name="work"></param>
        /// <param name="OTime"></param>
        /// <param name="Owork"></param>
        /// <param name="time"></param>
        /// <param name="Sign"></param>
        /// <returns></returns>
        public static bool InsertByAtt(int id,DateTime STime,int work,DateTime OTime,int Owork,string time,int Sign)
        {
            string sql = $"insert into Attendances(StaffId,SignTime,[Work],OutTime,OffWork,WorkTime,SignStatus) VALUES ({id},'{STime}',{work},'{OTime}',{Owork},'{time}',{Sign})";
            return DBHelper.GetExecuteNonQuery(sql);
        }

然后是公告和留言功能
两个功能差不多一个是读取txt,一个是保存txt
创建公告文件夹Announcement,里边保存一个测试文件Announcement.txt
读取功能

        //读取公告
        public string SelectAnnouncement()
        {
            string file = "../../Announcement/Announcement.txt";
            FileStream fs = new FileStream(file,FileMode.Open,FileAccess.Read);
            StreamReader sr = new StreamReader(fs);
            string txt = sr.ReadToEnd();
            sr.Close();
            fs.Close();
            return txt;
        }

创建io文件流读取文件
留言功能
创建留言文件夹leave a message

            string txt = LAMessage.Text;
            if (txt != "")
            {
                string time = DateTime.Now.Ticks.ToString();
                string txtname = "../../leave a message/" + (radioButton1.Checked ? User.id.ToString() + "-" + time : time) + ".txt";
                WriterTxt(txtname,txt);
                MessageBox.Show("留言成功!", "提示", MessageBoxButtons.OK);
                LAMessage.Text = "";
            }
            else
            {
                MessageBox.Show("留言为空!", "提示", MessageBoxButtons.OK);
            }
        //写入文件
        public void WriterTxt(string txtname,string txt)
        {
            FileStream fs = new FileStream(txtname,FileMode.Create,FileAccess.Write);
            StreamWriter sw = new StreamWriter(fs);
            sw.Write(txt);
            sw.Close();
            fs.Close();
        }

判断是否匿名,匿名则使用时间码开头,实名则使用用户id开头,这里可以延伸一个查看用户留言功能,时间问题笔者未实现
在一个是公司通讯录,就是一个绑定数据

        /// <summary>
        /// 查询电话簿
        /// </summary>
        /// <returns></returns>
        public static DataTable SelectByPhone()
        {
            string sql = "SELECT Department,Post,Name,Phone FROM Staffs s,Posts p WHERE s.PostNum = p.PostNum";
            return DBHelper.GetDataTable(sql);
        }

|
|
|
以上就是此项目全部内容
在这里插入图片描述
一个小项目,但是开发过程收获颇多,有复习也有学习课程以外的,果然实战是提升实力最快的方法,遂写出此文,与诸位共勉,也以此留念。

  • 35
    点赞
  • 182
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
非常好的主题!C# WinForm 人事管理系统可以帮助公司或组织管理员工的信息、薪资、考勤和福利等方面的内容。以下是一个简单的人事管理系统的设计思路: 1. 数据库设计:创建一个包含员工信息的数据库表,可以包括姓名、性别、出生日期、职位、部门、入职日期、联系方式等字段。另外还可以创建其他相关表,如部门表、职位表等,用于提高数据的组织性和可读性。 2. 登录界面:创建一个登录界面,要求用户输入用户名和密码来验证身份。可以使用数据库中的用户表来进行身份验证。 3. 主界面:登录成功后,显示主界面,其中包含菜单栏和工具栏。菜单栏可以包括员工信息管理、薪资管理、考勤管理、福利管理等功能模块。工具栏可以放置常用操作按钮,如添加员工、删除员工、编辑员工等。 4. 员工信息管理:提供添加、编辑、删除和查询员工信息的功能。可以通过表格或列表展示员工信息,并提供查询和排序功能。当点击某个员工时,可以显示其详细信息,并允许对其进行编辑或删除操作。 5. 薪资管理:提供设置员工薪资、调整薪资和查询薪资等功能。可以根据员工的职位和级别来设定基本工资,并允许进行个别调整。同时,可以根据时间范围查询员工的薪资历史记录。 6. 考勤管理:提供员工签到、签退和请假等功能。可以记录员工每天的上班时间和下班时间,并计算出勤情况。同时,可以记录员工的请假信息,并计算请假时长。 7. 福利管理:提供设置员工福利和查询福利信息的功能。可以设定各种福利,如补贴、奖金等,并为每个员工分配对应的福利。同时,可以查询员工的福利历史记录。 以上是一个简单的人事管理系统的设计思路,你可以根据需要进行功能的扩展和细化。希望这些信息对你有所帮助!如果你有任何其他问题,欢迎继续提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PROBIE_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值