基于JavaEE的智能OA办公系统的设计与实现

基于JavaEE的智能OA办公系统的设计与实现


文章目录


1.前言

本套系统主要实现的是OA办公系统中一些常用发功能,还有一些功能因为时间的原因没有开发,但主线功能基本完善。前端页面模板用的是GitHub上面一位老兄OA系统办公模板,但功能上面的话我都进行了重新编写,用自己的代码方式改了过来,并添加了很多功能点。

2.准备工作

开发环境:MySQL8.0,JDK1.8,Tomcat9.0

开发工具:eclipse2020,Navicat15,HBuilder X,vscode。

开发语言:java、html、css、javascript

第三方工具库:hutool、fastjson、jstl、bootstrap、jQuery、echarts、font-awesome

3.项目结构

基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现

4.部分界面截图

基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现
基于JavaEE的智能OA办公系统的设计与实现

由于系统的页面很多,就不一一截图了,总体的功能较为丰富,主线功能都完成。在人事管理中心的一个管理界面,对员工的一些信息进行了数据可视化分析,用到了echarts.js库,主要是把员工的学历、居住地、毕业学校、专业的数据进行了分析。

5.部分代码分析

5.1.1 ajax登录和添加员工功能

这部分实现的原理在《基于JavaEE(JSP)的共享资料平台的设计与实现》的第五点有说明,在这边就不继续介绍了。其JS代码部分基本上都是一样,唯一有差别的就是在jsp文件中,表单的格式不一样罢了。

5.1.2 考勤打卡功能

这是打卡日历图的代码,实现的功能为打卡日历图的显示,上班打卡一次,刷新一次会报红,因为没有继续打卡第二次,只有下班打卡后才会显示正常,下面环形也会显示全勤。日历图展示的时间数据是取最早打卡记录和最晚打卡记录,

数据库查询语句为SELECT EMP_ID,ATT_DATE,min(ATT_SIGNIN) as ATT_SIGNIN,max(ATT_SIGNOUT) as ATT_SIGNOUT from ATTENDANCE GROUP BY ATT_DATE,就用到了最大的时间和最小的时间。

    /**
	打卡日历模块
 */(function(undefined) {var _global;//工具函数//配置合并function extend(def, opt, override) {for(var k in opt) {if(opt.hasOwnProperty(k) && (!def.hasOwnProperty(k) || override)) {
				def[k] = opt[k]}}return def;}//日期格式化function formartDate(y, m, d, symbol) {
		symbol = symbol || '-';
		m = (m.toString())[1] ? m : '0' + m;
		d = (d.toString())[1] ? d : '0' + d;return y + symbol + m + symbol + d	}function Schedule(opt) {var def = {},
			opt = extend(def, opt, true),
			curDate = opt.date ? new Date(opt.date) : new Date(),
			year = opt.selectYear || curDate.getFullYear(),
			month = opt.selectMonth || curDate.getMonth(),
			day = curDate.getDate(),
			currentYear = curDate.getFullYear(),
			currentMonth = curDate.getMonth(),
			currentDay = curDate.getDate(),
			selectedDate = '',//缺勤
			qqDate = opt.qqDate || "",
			zcDate = opt.zcDate || "",
			el = document.querySelector(opt.el) || document.querySelector('body'),
			_this = this;var bindEvent = function() {//			if(el.dataset['cc']){
				el.addEventListener('click', function(e) {switch(e.target.id) {case 'nextMonth':
							_this.nextMonthFun();break;case 'nextYear':
							_this.nextYearFun();break;case 'prevMonth':
							_this.prevMonthFun();break;case 'prevYear':
							_this.prevYearFun();break;default:break;};if(e.target.className.indexOf('currentDate') > -1) {
						opt.clickCb && opt.clickCb(year, month + 1, e.target.innerHTML,e.target);
						selectedDate = e.target.title;
						day = e.target.innerHTML;render();}}, false)//				el.dataset['cc']=1;//			}}var init = function() {var scheduleHd = '<div class="schedule-hd">' +'<div>' +'<span class="arrow icon iconfont icon-112leftarrowhead" id="prevMonth"></span>' +'</div>' +'<div class="today">' + year + "年" + (month + 1) + "月" + '</div>' +'<div>' +'<span class="arrow icon iconfont icon-111arrowheadright" id="nextMonth"></span>' +'</div>' +'</div>'var scheduleWeek = '<ul class="week-ul ul-box clearfix">' +'<li>日</li>' +'<li>一</li>' +'<li>二</li>' +'<li>三</li>' +'<li>四</li>' +'<li>五</li>' +'<li>六</li>' +'</ul>'var scheduleBd = '<ul class="schedule-bd ul-box clearfix" ></ul>';
			el.innerHTML = scheduleHd + scheduleWeek + scheduleBd;bindEvent();render();}var render = function() {var fullDay = new Date(year, month + 1, 0).getDate(), //当月总天数
				startWeek = new Date(year, month, 1).getDay(), //当月第一天是周几
				total = (fullDay + startWeek) % 7 == 0 ? (fullDay + startWeek) : fullDay + startWeek + (7 - (fullDay + startWeek) % 7), //元素总个数
				lastMonthDay = new Date(year, month, 0).getDate(), //上月最后一天
				eleTemp = [];for(var i = 0; i < total; i++) {if(i < startWeek) {var nowDate = formartDate(year, month, ((lastMonthDay - startWeek) + 1 + i), '-');var addClass = '';
					
					eleTemp.push('<li class="other-month"><span class="dayStyle ' + addClass + '">' + (lastMonthDay - startWeek + 1 + i) + '</span></li>')} else if(i < (startWeek + fullDay)) {var nowDate = formartDate(year, month + 1, (i + 1 - startWeek), '-');var addClass = '';var attSignin = '';var attSignout = '';//					selectedDate == nowDate && (addClass = 'selected-style');for(var j = 0; j < zcDate.length; j++) {
						zcDate[j].attDate == nowDate && (addClass = 'zc_day', attSignin = zcDate[j].attSignin, attSignout = zcDate[j].attSignout);}for(var z = 0; z < qqDate.length; z++) {
						qqDate[z].attDate == nowDate && (addClass = 'qq-style', attSignin = qqDate[z].attSignin, attSignout = qqDate[z].attSignout);}//					formartDate(currentYear,currentMonth+1,currentDay,'-') == nowDate && (addClass = 'today-flag');
					eleTemp.push('<li class="current-month" ><span  class="currentDate dayStyle ' + addClass + '">' + (i + 1 - startWeek) + '</span><div class="day_time"><div>上班:' + attSignin + '</div><div>下班:' + attSignout + '</div></li>')} else {
					eleTemp.push('<li class="other-month"><span class="dayStyle">' + (i + 1 - (startWeek + fullDay)) + '</span></li>')}}
			el.querySelector('.schedule-bd').innerHTML = eleTemp.join('');
			el.querySelector('.today').innerHTML = year + "年" + (month + 1) + "月";};this.nextMonthFun = function() {if(month + 1 > 11) {
					year += 1;
					month = 0;} else {
					month += 1;}render();
				opt.nextMonthCb && opt.nextMonthCb(year, month + 1, day);},this.nextYearFun = function() {
				year += 1;render();
				opt.nextYeayCb && opt.nextYeayCb(year, month + 1, day);},this.prevMonthFun = function() {if(month - 1 < 0) {
					year -= 1;
					month = 11;} else {
					month -= 1;}render();
				opt.prevMonthCb && opt.prevMonthCb(year, month + 1, day);},this.prevYearFun = function() {
				year -= 1;render();
				opt.prevYearCb && opt.prevYearCb(year, month + 1, day);}init();}//将插件暴露给全局对象
	_global = (function() {return this || (0, eval)('this')}());if(typeof module !== 'undefined' && module.exports) {
		module.exports = Schedule;} else if(typeof define === "function" && define.amd) {define(function() {return Schedule;})} else {!('Schedule' in _global) && (_global.Schedule = Schedule);}}());

5.1.3 ajax与Bootstrap Table实现表格内容的读写与分页

这里的列表代码以部门管理为例进行演示。

这是前端table的写法,主要是把表头给写出来,然后利用bootstrap的写法,在js里面写实现方法,还是比较方便的。下面就来一一讲解实现过程。

<table id="datagrid" class="table table-bordered table-striped table-hover"><thead><tr><th data-width="60" data-align="center"data-formatter="indexFormatter">#</th><th data-field="deptId" data-align="center">部门编号</th><th data-field="deptName" data-align="center">部门名称</th><th data-field="deptUserid" data-align="center">负责人</th><th data-field="deptCreatetime" data-align="center" data-formatter="dateFormatter">创建时间</th><th data-field="deptId" data-class="p-1" data-width="150"data-align="center" data-formatter="optionFormatter">操作</th></tr></thead></table>

CLICK ME

```java

然后在其他<th></th>标签中,有一个data-field属性,这里表达的是这一列所需要的展示的值,也就是从数据库返回的值显示的内容。

在创建时间的<th></th>的标签里面,写了一个data-formatter="dateFormatter",和上面的原理一样,在下面的js代码找到dateFormatter函数。主要是对数据库传来的时间进行格式化展示。

/**
	装载下拉框的角色列表
 */var searchDeptForm = $(document.forms.searchDeptForm);var searchDeptForms = document.forms.searchDeptForm;//表单提交事件searchDeptForms.onsubmit = ()=>{
	$.post(
		searchDeptForms.action,
		sys.form.param(searchDeptForms).toString(),function(data){if(data.code==200){//sys.js库中定义的方法,可以弹出提示界面
				sys.toastr.success("查询成功");}else{
				sys.toastr.error(data.message);}},"json");return false;}// 获取部门下拉框的数据var deptIdSelect = $(document.forms.searchDeptForm.deptIdSelect);$.get("department.let?action=listDepts",function(data){for(let dept of data) {
			deptIdSelect.append(`<option value="${dept.deptId}">${dept.deptName}</option>`);}},"json");//保存查询条件var searchParams = new URLSearchParams();searchDeptForm.on("submit",function(){
	searchParams = sys.form.param(searchDeptForm[0]);
	datagrid.bootstrapTable("refresh",{pageNumber:1});return false;});/**
	表格内容填充方式,从数据库请求的数据,填充到表格中
*/var datagrid = $("#datagrid").bootstrapTable({
	url: "department.let?action=page",
	dataField: "list",//rows
	totalField: "total",
	queryParamsType: "",//limit
	pagination: true,
	sidePagination: "server",//client
	queryParams:function(params) { 
	  for(let name of searchParams.keys()){//添加搜索条件
		  params[name]=searchParams.get(name);
	  }
	  return params }});/**
	列表最前面加索引
 */var indexFormatter = function(value, row, index, fieldName) {return index + 1;}/**
	给时间设置0
 */function addZero(n) {return n < 10 ? '0' + n : '' + n;};/**
	格式化时间
 */var dateFormatter = function(value, row, index, fieldName) {var date = new Date(value);var time = date.getFullYear() + "-" + addZero(date.getMonth() + 1) + "-" + addZero(date.getDate());return time;}/**
 * 格式化表格操作菜单
 */var optionFormatter = function(value, row, index) {return `<div class="dropdown">
  <button class="btn btn-outline-primary btn-block dropdown-toggle" type="button" data-toggle="dropdown">
    操作
  </button>
  <div class="dropdown-menu dropdown-menu-right">
    <a class="dropdown-item"   href="department.let?action=getDeptInfo&deptId=${value}">查看&修改</a>
    <a class="dropdown-item _delete" data-index="${index}" href="javascript:void(0);">删除</a>
  </div>
</div>`;}/**
	操作按钮
 */datagrid.on("click", "._delete", function() {//删除let obj = $(this);let index = obj.data("index");//data-indexlet row = datagrid.bootstrapTable("getData")[index];
	sys.confirm(`您确定要删除[${row.deptName}]吗?`, function(r) {if (r) {
			$.post("department.let?action=delete",{ "deptId": row.deptId },function(data) {if (data.code == 200) {
						sys.toastr.success(`删除用户[${row.deptName}]成功`);
						datagrid.bootstrapTable("refresh");} else {
						sys.toastr.error(data.message);}}, "json");}});});

5.1.4 员工各类信息统计Echarts代码

环状比例图的实现代码,主要用的是echarts库。传入的数据有三种TextData,DigitalData,titleText,分别为图例数据、员工详情数据、该图形的标题数据。

function EmpCityChart(TextData,DigitalData,titleText) {var myChart = echarts.init(document.getElementById('empCity'));var img = 'data:image/png;base64,iVBORw0K....VORK5CYII=';var trafficWay = DigitalData;var data = [];var color=['#00ffff','#00cfff','#006ced','#ffe000','#ffa800','#ff5b00','#ff3000']for (var i = 0; i < trafficWay.length; i++) {data.push({value: trafficWay[i].value,name: trafficWay[i].name,itemStyle: {normal: {borderWidth: 5,shadowBlur: 20,borderColor:color[i],shadowColor: color[i]}}}, {value: 2,name: '',itemStyle: {normal: {label: {show: false},labelLine: {show: false},color: 'rgba(0, 0, 0, 0)',borderColor: 'rgba(0, 0, 0, 0)',borderWidth: 0}}});}var seriesOption = [{name: '',type: 'pie',clockWise: false,radius: [105, 109],hoverAnimation: false,itemStyle: {normal: {label: {show: true,position: 'outside',color: '#ddd',formatter: function(params) {var percent = 0;var total = 0;for (var i = 0; i < trafficWay.length; i++) {total += trafficWay[i].value;}percent = ((params.value / total) * 100).toFixed(0);if(params.name !== '') {return titleText+':' + params.name + '\n' + '\n' + '占百分比:' + percent + '%';}else {return '';}},},labelLine: {length:30,length2:100,show: true,color:'#00ffff'}}},data: data}];option = {backgroundColor: '#5e7c85',color : color,title: {text:titleText,top: '48%',textAlign: "center",left: "49%",textStyle: {color: '#fff',fontSize: 22,fontWeight: '400'}},graphic: {elements: [{type: "image",z: 3,style: {image: img,width: 178,height: 178},left: 'center',top:  'center',position: [100, 100]}]},tooltip: {show: false},legend: {icon: "circle",orient: 'horizontal',x: 'center',data:TextData,top: 10,align: 'left',textStyle: {color: "#fff"},itemGap: 20},toolbox: {show: false},series: seriesOption}myChart.setOption(option);}

ajax请求数据,这里请求的数据是员工的岗位数据分析数据,将获得的数据进行格式的一些转换,然后调用上面的函数,就可以正常显示图形了。

function empDegree(){
	$.get("chartData.let?action=getEmpDegreeData",function(data){var empText=[];var empData=[];for(let i=0;i<data.data.length;i++){
				empText.push(data.data[i].empTiptopdegree);
				empData.push({name:data.data[i].empTiptopdegree,value:data.data[i].empData*100});}EmpCityChart(empText,empData,"最高学历")},"json");}

servlet实现获取数据。这里写了SQL语句是因为在service和dao包中的实现类用的是同一个方法,只需要传入不同的SQL语句和异常代号和异常信息就可以了。

private ChartDataService chartDataService = new ChartDataServiceImpl();/**
* -获取员工的学历数据
* @return
*/public R<?> getEmpDegreeData(){String sql = "select EMP_TIPTOPDEGREE,COUNT(EMP_TIPTOPDEGREE) as EMP_DATA from EMPLOYEE GROUP BY EMP_TIPTOPDEGREE";List<Employee> empData = chartDataService.getEmpData(sql,1001,"获取员工学历数据失败");return R.ok(empData);}

ChartDataDAO.java实现

/**
* -由于代码非常相似,所以只需要根据SQL语句不同查询不同的字段就行,这里查询的是员工的数据
* @return
* @throws SQLException 
*/public List<Employee> getEmpData(String sql) throws SQLException {List<Object> params = new ArrayList<Object>();return DBUtil.list(sql, Employee.class, params.toArray());}

6.总结

以上的系统主线功能基本完成,从GitHub大佬用的模板改进了全部功能,现在找不到这个GitHub的地址是什么了,如果有侵权请联系。用来学习的话,能够把全部代码总一遍,基本上可以学会所有的javaee知识,如果JavaScript底子好的话,还能够深入的研究一下ajax代码。ajax代码比传统MVC好用多了。

好了,这次的项目分享到此也差不多了。觉得小弟写的不错的话,可以点赞加关注一下,谢谢!有需要源码的小伙伴也可以搜索企鹅号863772270,编码不易,请作者喝瓶水即可。

DataDAO.java实现

/**
* -由于代码非常相似,所以只需要根据SQL语句不同查询不同的字段就行,这里查询的是员工的数据
* @return
* @throws SQLException 
*/public List<Employee> getEmpData(String sql) throws SQLException {List<Object> params = new ArrayList<Object>();return DBUtil.list(sql, Employee.class, params.toArray());}

6.总结

以上的系统主线功能基本完成,从GitHub大佬用的模板改进了全部功能,现在找不到这个GitHub的地址是什么了,如果有侵权请联系。用来学习的话,能够把全部代码总一遍,基本上可以学会所有的javaee知识,如果JavaScript底子好的话,还能够深入的研究一下ajax代码。ajax代码比传统MVC好用多了。

好了,这次的项目分享到此也差不多了。觉得小弟写的不错的话,可以点赞加关注一下,谢谢!有需要源码的小伙伴也可以搜索企鹅号863772270,编码不易,请作者喝瓶水即可。

上一篇:FTL——磨损平衡


下一篇:SSD的FTL——深入浅出SSD笔记