简介:一套基于C#与WPF的企业OA系统完整源码,采用C/S架构,面向Windows环境,基于Visual Studio 2012与SQL Server 2008R2构建,适合.NET桌面开发者、企业内部系统二次开发人员以及计算机相关专业毕业设计学生。系统功能共分四大模块、17个子功能,覆盖审批中心(人事/项目/公文申请与待办审核)、计划中心、日程管理、考勤记录、劳资管理、人事客户管理、内置记事本和其他小工具,逻辑完整,能体现OA系统的典型业务闭环。压缩包共582个文件,包含253个cs源码文件、38个xaml界面文件、74个baml编译资源,以及76个ico图标、48个png图片、dll与exe运行库和config配置文件,还附带mdf/ldf数据库备份,整体仅17.3MB,目录结构清晰,便于按模块查阅。资源已有790人学习/浏览,对WPF桌面应用开发者有较好参考价值。借助完整源码和数据库文件,可以快速理解WPF客户端如何实现流程审批、数据绑定、权限角色划分等核心机制,既能用于课程设计,也能作为企业级OA二次开发的基础框架。
1. 这套WPF企业OA系统源码,适合谁用、能解决什么
公司要上一套办公系统,又不想把人事、财务数据放到云端,这时候一套能本地跑的 WPF 企业 OA 系统源码就特别有参考价值。这套源码是 C/S 桌面架构,VisualStudio 2012 写客户端,SQLServer 2008R2 存数据,功能拆成 4 大模块、17 个子模块,覆盖审批中心、计划、日程、考勤、劳资、人事、客户管理和记事本。适合两类人:一是做课程设计或毕设、需要一个完整业务闭环的学生;二是想在内网部署办公系统、打算做二次开发的一线工程师。我拆包时先验证它能不能跑通,再逐模块摸实现边界,下面按复现顺序讲。
2. 工程结构与技术栈:从BAML文件反推这套OA的实现边界
拿到资源先别急着双击OASystem.application,先把技术栈和文件构成看清楚。这套系统是典型的 2012 年 Windows 桌面办公软件形态,客户端、数据库、部署方式都跟现在的 Web OA 思路不一样,搞清楚这些,后面改起来才知道往哪使劲。
2.1 为什么是 WPF + SQL Server 2008 R2 这个组合
WPF 在那个时候做桌面客户端,比 WinForms 强在 XAML 描述界面、数据绑定和控件模板。审批表单这种动态界面,字段多、状态多,用 WinForms 拖控件后期维护很痛苦,WPF 可以把表单布局和业务逻辑拆开,改 UI 不用动后端代码。这套系统里数据绑定用得很频繁,登录用户信息、待办列表、考勤统计都走绑定,你后续改界面时尽量保持绑定路径不变,能少踩很多坑。
C/S 模式对 OA 来说一直有争议,但内网办公场景里它有实际优势:数据不出内网、离线也能操作、打印直接调本机控件。SQLServer 2008R2 选型则跟 Windows 域环境强相关,企业内网已有 SQL Server 部署时,直接附加 mdf 就能跑,不用额外搭一套数据库服务。
对现在的人来说,这套源码的价值在于它是一个完整的业务系统,不是 demo。你打开工程能看到登录、权限、菜单、审批流、考勤统计这些模块互相咬合,比单学 WPF 控件用法更能理解企业级桌面软件的代码组织方式。
2.2 从文件清单看工程构成:application、baml、bak 各是什么
解压后文件清单不长,但每个文件都代表一个环节:
| 文件 | 类型 | 作用 |
|---|---|---|
| OASystem.application | ClickOnce 部署清单 | 描述应用入口、版本、依赖程序集,双击它可发布安装 |
| OASystem.vshost.application | VS 调试宿主清单 | Visual Studio 调试时自动生成的宿主配置 |
| OASystem.application.bak | 清单备份 | VS 发布时对旧清单的自动备份 |
| MainWindow.baml | 主窗口编译产物 | 主窗体布局和逻辑,出现两次是编译输出重复收录路径 |
| RSDAK_handle.baml | 人事档案处理窗口 | 按命名习惯推测是“人事档案”拼音缩写,处理人事申请和档案 |
| NOTEPAD.baml | 内置记事本窗口 | 系统设置里带的简易记事本 |
| JHZX.baml | 计划中心窗口 | “计划中心”拼音缩写,对应经理权限的计划管理 |
BAML 是 Binary Application Markup Language,XAML 编译成二进制后的资源格式,运行时由 BAML 加载器解析成界面树。这里有个关键点:改 UI 不能直接改 baml 文件,要回 XAML 源码重新编译才会重新生成 baml。如果包里只有 baml 没有 xaml,用 ILSpy 加载主程序集,找到对应窗口节点,右键导出 xaml,导出来再改,否则改完一编译就被覆盖。
2.3 数据库连接串:实例名、账号、数据源三个坑
客户端连数据库靠连接串,默认配置在app.config里:
<connectionStrings> <add name="OASystem.ConnectionString" connectionString="Data Source=.;Initial Catalog=OASystem;User ID=sa;Password=123456;" providerName="System.Data.SqlClient"/> </connectionStrings>Data Source=.表示本机默认实例,只在开发机有效;部署到别的机器要改成机器名或机器名\SQLEXPRESS这种命名实例写法。Initial Catalog=OASystem是数据库名,附加 mdf 时记得保持逻辑名称一致,否则运行时直接报“无法打开数据库”。
C# 里读取连接串的写法很固定:
string connStr = ConfigurationManager.ConnectionStrings["OASystem.ConnectionString"].ConnectionString; using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); // 连接成功后,后续 SqlCommand、SqlDataAdapter 都复用 conn }注意ConfigurationManager要引用System.Configuration.dll,不然编译不过。连接串改成 SQL Server 身份认证后,数据库实例本身必须开启混合验证模式,否则sa永远登录失败。SSMS 里执行下面这条,把登录模式改成混合:
EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2; GO改完重启 SQL Server 服务生效。如果不想用sa,连接串换成Integrated Security=True并去掉User ID和Password,用 Windows 身份认证,但那样部署到别的机器要额外配域账号,办公室环境一般不值当。
3. 核心业务模块拆解:审批、计划、考勤、劳资的数据流转
登录进去以后,最能体现 OA 价值的是审批中心和考勤这两块。审批中心处理三类申请(人事、项目、公文),考勤关联签到和工资出账,这两块的表设计和权限判断直接决定系统好不好用。
3.1 审批中心:三类申请与待办流转的状态设计
新建工作流要先选类型,再填对应表单,提交后写入待办表;待办工作流则按角色拉取当前用户可审核的流程。常规做法是建一张Workflow主表,字段包括流程类型、申请人、申请时间、状态、当前审核角色。
查询待办工作流的核心方法我一般这样写:
public DataTable GetPendingApprovals(string role) { // Status: 0=待审, 1=通过, 2=驳回 // ManagerRole: 存储当前节点需要的审核角色 string sql = @"SELECT wf.FlowID, wf.ApplyType, wf.ApplyUser, wf.ApplyTime, wf.Status FROM Workflow wf WHERE wf.Status = 0 AND wf.ManagerRole = @role ORDER BY wf.ApplyTime DESC"; SqlParameter[] parameters = { new SqlParameter("@role", role) }; return SqlHelper.ExecuteDataTable(sql, parameters); }ManagerRole字段是流转判断的关键。人事申请的角色是人事经理,项目申请的角色是项目经理,公文申请的角色是办公室主任,审核人提交结论时把Status更新成 1 或 2,同时记录Auditor和AuditTime,这样待办列表只查Status = 0的数据,效率高,逻辑也直观。
边界在角色字符串必须跟登录用户的权限角色完全一致,多一个空格都查不出来。我排查过类似问题,最后发现是角色存入时带了换行符,SQL 里用LTRIM(RTRIM(wf.ManagerRole))才解决。
3.2 计划中心与日程管理:权限边界和日期查询写法
计划中心面向经理权限,经理制定计划并管理,计划类型可以扩展为销售计划、年度业绩等。日程管理则是全员功能,用户能定某天日程、查历史日程。权限边界在菜单初始化时就定死了,非经理用户看不到计划中心入口,这个判断通常写在主窗体的菜单加载逻辑里。
日程查询最容易踩的坑是日期范围。很多人喜欢用BETWEEN @start AND @end,但日程的BeginTime带时间部分,BETWEEN会漏掉当天 23:59:59 之后的记录。我一般用区间写法:
SELECT ScheduleID, Title, BeginTime, EndTime FROM Schedule WHERE UserID = @uid AND BeginTime >= @startDate -- 结束日期加一天,用开区间过滤当天最后一秒 AND BeginTime < DATEADD(day, 1, @endDate) ORDER BY BeginTime;@startDate和@endDate只传日期部分,例如2025-01-01,SQL 里加一天后当作开区间上限,这样能完整覆盖当天所有日程。这个写法在考勤统计里同样适用,下一条就会遇到。
3.3 考勤与劳资:签到统计与工资出账的字段关系
考勤管理分两部分:职员每天上下班签到,人事权限管理者调取、查阅、统计出勤记录。签到表一般至少三列可以支撑后续统计:UserID、SignDate、SignTime,如果能区分上下午就再加IsMorning标记。
月度出勤统计 SQL 我通常写到字段级聚合:
SELECT UserID, SUM(CASE WHEN IsMorning = 1 AND SignTime <= '09:00' THEN 1 ELSE 0 END) AS OnTimeDays, SUM(CASE WHEN IsMorning = 1 AND SignTime > '09:00' THEN 1 ELSE 0 END) AS LateDays FROM Attendance WHERE SignDate >= @monthStart AND SignDate < DATEADD(month, 1, @monthStart) GROUP BY UserID;@monthStart传当月第一天的日期,DATEADD(month, 1, @monthStart)取下月第一天,两个条件夹出整月数据,避免手工算“月末最后一天”出错。劳资模块的工资出账统计就基于这类聚合结果,再关联职工基本工资表,按UserID汇总生成出账记录。财务或经理权限才能看到这部分,权限判断放在窗体加载事件里,用if (!HasPermission("LaborWage"))直接拦掉。
4. 避坑与排错:数据库、权限、部署现场的五个教训
复现这套系统时翻车点集中在数据库附加、权限菜单、部署清单和日期边界上。下面五条都是实际遇到过的现场,每一条都按现象、原因、解决讲清楚。
4.1 数据库附加失败:文件权限与引擎版本两重原因
现象:SSMS 附加 mdf 时报“无法检索此数据库的版本”,或者提示“拒绝访问”。 原因:SQLServer 2008R2 的 mdf 主版本号是 10.50,用更高版本 SQL Server 附加一般没问题,反过来用低版本附加高版本文件才会报版本错误;拒绝访问多半是 mdf 文件所在目录没有给 SQL Server 服务账户读取权限。 解决:先确认本机 SQL Server 版本不低于 2008R2,再把 mdf 和 ldf 放到C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA目录下,给该目录加上MSSQLSERVER服务账户的完全控制权限,重新附加即可。ldf 日志文件缺失时,SSMS 附加界面里先移除日志文件再附加,会自动重建。
4.2 登录后菜单空白:权限配置表和功能表对不上
现象:账号能登录,但主界面菜单栏只有“系统”一项,其他模块全部消失。 原因:菜单显示由权限配置表控制,新建账号默认角色没绑定任何功能模块,而初始化的管理员账号又可能因为角色字符串跟代码里的常量不一致,导致HasPermission判断全部返回 false。 解决:进数据库看权限配置表,找RoleName字段,检查代码里GetPendingApprovals(string role)这类方法传入的角色值,比如“部门经理”不要写成“部门经理 ”(带空格),把配置表里的角色名跟代码常量对齐;再确认账号表里的RoleID字段关联到了正确角色,重新登录即可。
4.3 ClickOnce 部署失败:证书与清单版本冲突
现象:双击OASystem.application,安装进度走一半提示“清单签名无效”或“部署标识不匹配”。 原因:VS2012 默认用临时生成的测试证书签名,换机器部署时证书过期或清单版本号冲突,ClickOnce 校验不过就中断。 解决:打开项目属性 →“签名”页,取消“为 ClickOnce 清单签名”选项(内网用没必要签),重新发布;发布页里把“更新”改成“从不检查更新”,规避版本号冲突报错。如果客户端已经装过旧版,先在“控制面板 → 程序和功能”里卸载干净再装新版。
4.4 WPF 窗口白屏或闪退:BAML 资源 ID 解析失败
现象:主窗口能弹出,但某按钮点击后窗口白屏或直接崩溃,异常信息串里有BAML ID相关字样。 原因:包里只有 baml 而你在源码里改了 xaml 后未重新编译,运行时加载的 baml 资源 ID 与程序集不匹配;或者用 ILSpy 导出 xaml 时依赖的命名空间被改了。 解决:不要直接编辑 baml,回到 VS 里改 xaml,重新生成整个工程。如果原包没有 xaml 源码,先反编译导出,再把导出的 xaml 文件加入工程,设置为Page或ApplicationDefinition,确保x:Class的命名空间跟代码文件一致,再编译发布。
4.5 考勤统计错一天:写死 23:59:59 的悲剧
现象:月末考勤统计,最后一天全部显示缺勤,其它日期正常。 原因:统计代码里把结束时间写死为@endDate + ' 23:59:59',SQLServer 里datetime的精度到 3 毫秒,严格意义上23:59:59.999依然不等于23:59:59,边界记录被漏掉。 解决:统一改成开区间写法,结束日期用DATEADD(day, 1, @endDate),查询条件写成SignTime < @nextDay,这样当天最后一毫秒的记录也能命中。从那以后我只要写日期范围查询,一律先问自己一句:这个条件边界会不会漏数据。
5. 从跑通到二次开发:自定义审批表单的完整改法
5.1 五步验证流程
拿到源码后按这个顺序验证:附加数据库 → 修改连接串 → 用管理员账号登录 → 在审批中心提交一条“人事申请” → 切到管理员账号审核这条记录。跑通这三个节点,就说明数据库、登录权限、审批流三个核心链路都正常。能熟练走完一遍后,再动手改任何模块,心里都有底。
5.2 新增审批表单要动的六个位置
设计一个“费用申请”表单,需要动的地方是:新建费用申请 xaml 窗口、在Workflow表新增对应ApplyType枚举值、下拉框加选项、审批中心的判断逻辑加分支、ManagerRole值指定为财务经理、菜单权限里给财务角色开放入口。其中漏掉第四或第五步最常见,表现为表单能提交但待办里永远看不到。
5.3 日程提醒:用一个 DispatcherTimer 轮询
最后给个能直接用的小技巧,日程管理里做提醒,用DispatcherTimer而不是Thread.Sleep:
DispatcherTimer timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMinutes(1); timer.Tick += (s, e) => CheckScheduleRemind(); timer.Start();DispatcherTimer跑在 UI 线程上,回调里可以直接操作窗口控件,不会像Thread.Sleep那样卡死界面或抛线程间调用异常。间隔设 1 分钟足够支撑按分钟粒度的日程提醒,频繁到秒级反而给客户端增加没必要的开销。我接手过的几个 OA 项目提醒功能翻车,都是因为用了后台线程定时器再到处Invoke,换成DispatcherTimer后代码短一半,问题也没了。
之前赶进度把日期区间写成BETWEEN,月末最后一天的数据始终查不出来,排查半宿才发现是边界精度问题。那次以后我改任何 OA 的查询条件,都强制把23:59:59这种写法换成开区间,再塞几条边界数据验证才敢合代码。希望帮到你。
本文还有配套的精品资源,点击获取