
简介一套基于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.applicationClickOnce 部署清单描述应用入口、版本、依赖程序集双击它可发布安装OASystem.vshost.applicationVS 调试宿主清单Visual Studio 调试时自动生成的宿主配置OASystem.application.bak清单备份VS 发布时对旧清单的自动备份MainWindow.baml主窗口编译产物主窗体布局和逻辑出现两次是编译输出重复收录路径RSDAK_handle.baml人事档案处理窗口按命名习惯推测是“人事档案”拼音缩写处理人事申请和档案NOTEPAD.baml内置记事本窗口系统设置里带的简易记事本JHZX.baml计划中心窗口“计划中心”拼音缩写对应经理权限的计划管理BAML 是 Binary Application Markup LanguageXAML 编译成二进制后的资源格式运行时由 BAML 加载器解析成界面树。这里有个关键点改 UI 不能直接改 baml 文件要回 XAML 源码重新编译才会重新生成 baml。如果包里只有 baml 没有 xaml用 ILSpy 加载主程序集找到对应窗口节点右键导出 xaml导出来再改否则改完一编译就被覆盖。2.3 数据库连接串实例名、账号、数据源三个坑客户端连数据库靠连接串默认配置在app.config里connectionStrings add nameOASystem.ConnectionString connectionStringData Source.;Initial CatalogOASystem;User IDsa;Password123456; providerNameSystem.Data.SqlClient/ /connectionStringsData Source.表示本机默认实例只在开发机有效部署到别的机器要改成机器名或机器名\SQLEXPRESS这种命名实例写法。Initial CatalogOASystem是数据库名附加 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 NHKEY_LOCAL_MACHINE, NSoftware\Microsoft\MSSQLServer\MSSQLServer, NLoginMode, REG_DWORD, 2; GO改完重启 SQL Server 服务生效。如果不想用sa连接串换成Integrated SecurityTrue并去掉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-01SQL 里加一天后当作开区间上限这样能完整覆盖当天所有日程。这个写法在考勤统计里同样适用下一条就会遇到。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:59SQLServer 里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.SleepDispatcherTimer 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这种写法换成开区间再塞几条边界数据验证才敢合代码。希望帮到你。本文还有配套的精品资源点击获取