登录账号:t6普通用户 t7部门经理 m8总经理 密码都为:test
多级请假:7级及以下申请请假需要部门经理审核,若是请假时长超过72小时,则需要总经理审核,7级申请请将需要总经理审核,总经理请假自动审核通过。申请和审核流程都需要生产消息通知。
(前后端不分离)
业务流程测试:王美美[高级研发工程师](7级以下用户)登录并申请请假(超过了72小时需要部门经理和总经理申请)
王美美请假(查过72小时),需要部门经理和总经理审核
部门经理(7级)登录消息通知审核消息并处理审核
部门经理审核通过
王美美查看部门经理审核结果
部门经理审核通过,需要总经理审核(8级)登录,首页消息通知审核通过
首页系统通知
审批通过
总经理审核通过,王美美查看审核结果
如果部门经理审核不通过,则总经理不需要处理也没有系统通知
登录页面
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>慕课网OA办公系统</title>
<!-- 引入样式 -->
<link rel="stylesheet" type="text/css" href="assets/element-plus/index.css">
<!-- 引入组件库 -->
<script src="/assets/vue/vue.global.js"></script>
<script src="/assets/element-plus/index.full.js"></script>
<script src="/assets/axios/axios.js"></script>
<style>
.login-box {
border: 1px solid #DCDFE6;
width: 350px;
margin: 180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title{
text-align: center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>
</head>
<body>
<div id="app">
<el-form ref="loginForm" label-width="80px" :rules="rules" :model="form" class="login-box">
<h2 class="login-title">慕课网OA办公系统</h2>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onSubmit('loginForm')" style="width:200px">登录</el-button>
</el-form-item>
</el-form>
</div>
<script>
const Main = {
data() {
return {
form: {
username: ''
,password: ''
}
,rules:{
username: [
{required: true,message : '账号不能为空' , trigger:'blur'}
],
password:[
{required: true,message : '密码不能为空' , trigger:'blur'}
]
}
}
}
,methods : {
onSubmit(formName){
const form = this.$refs[formName];
form.validate((valid) => {
if(valid){
console.info("表单校验成功,准备提交数据");
const form = this.form;
const $message=this.$message;
const params=new URLSearchParams();
params.append("username",form.username);
params.append("password",form.password);
axios.post("/api/login",params,{}).then(function(response){
console.info(response);
const json=response.data;
if(json.code==0){
sessionStorage.uid=json.data.user.userId;
sessionStorage.eid=json.data.user.employeeId;
window.location.href="/index.html"
}else{
$message.error({message:json.message,offset:100});
}
})
}
})
}
}
};
//初始化Vue,绑定Main中的数据,利用ElementPlus对#app容器进行重新渲染
const app = Vue.createApp(Main);
app.use(ElementPlus);
app.mount("#app");
</script>
</body>
</html>
首页
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>慕课网办公OA系统</title>
<!-- 引入样式 -->
<link rel="stylesheet" type="text/css" href="assets/element-plus/index.css">
<!-- 引入组件库 -->
<script src="/assets/vue/vue.global.js"></script>
<script src="/assets/element-plus/index.full.js"></script>
<script src="/assets/axios/axios.js"></script>
<style>
.el-header {
background-color: rgb(238, 241, 246);
color: #333;
line-height: 60px;
}
html,body,#app,.el-container {
padding: 0px;
margin: 0px;
height: 100%;
max-height: 100%;
}
</style>
</head>
<body>
<div id="app">
<el-container style="height:100%;border:1px solid #eee">
<el-header>
<el-row>
<el-col :span="12">
<span style="font-size: 18px;color:darkcyan">慕课网办公OA系统</span>
</el-col>
<el-col :span="12" style="text-align:right">
<el-dropdown>
<i class="el-icon-s-check" style="font-size:18px;margin-right: 15px">
<span style="margin-right: 15px">{{employee.name}}[{{employee.title}}]</span>
</i>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-on:click="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
</el-row>
</el-header>
<el-container>
<el-aside width="200px" style="max-height:100%;background-color: rgb(238, 241, 246)">
<!--默认展开第一个模块功能-->
<el-menu :default-openeds="['0']">
<template v-for="(n,idx) in nodeList">
<el-submenu :index="idx.toString()">
<template #title><i class="el-icon-s-tools"></i>{{n.node.nodeName}}</template>
<template v-for="func in n.children">
<el-menu-item :index="func.nodeId.toString()" v-on:click="showPage(func.url)">{{func.nodeName}}</el-menu-item>
</template>
</el-submenu>
</template>
</el-menu>
</el-aside>
<el-main>
<iframe id="main" name="main" src="/notice.html" style="width:100%;height:100%;border: 0px"></iframe>
</el-main>
</el-container>
</el-container>
</div>
<script>
const Main = {
data(){
return {
nodeList:[],
employee:{}
}
}
,methods:{
showPage(url){
document.getElementById("main").src = url;
}
,logout(){
sessionStorage.clear();
window.location.href = "/login.html";
}
}
,mounted(){
const objApp = this;
const eid = sessionStorage.eid;
const uid = sessionStorage.uid;
axios.get("/api/user_info?uid=" + uid + "&eid=" + eid)
.then(function(response){
const json = response.data;
json.data.nodeList.forEach(function (item){
objApp.nodeList.push(item);
})
console.info(objApp.nodeList);
objApp.employee = json.data.employee;
})
}
};
const app = Vue.createApp(Main);
app.use(ElementPlus);
app.mount("#app");
</script>
</body>
</html>
请假申请页面
leave_form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>请假申请单</title>
<!-- 引入样式 -->
<link rel="stylesheet" type="text/css" href="/assets/element-plus/index.css">
<!-- 引入组件库 -->
<script src="/assets/vue/vue.global.js"></script>
<script src="/assets/element-plus/index.full.js"></script>
<script src="/assets/element-plus/locale/zh-cn.js"></script>
<script src="/assets/axios/axios.js"></script>
<style>
.el-form {
border: 1px solid #DCDFE6;
width: 600px;
margin: 180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
</style>
</head>
<body>
<div id="app">
<el-form ref="leaveForm" :model="form" :rules="rules" label-width="80px">
<el-descriptions title="请假申请单" :column="1" border>
<el-descriptions-item label="部门">{{department.departmentName}}</el-descriptions-item>
<el-descriptions-item label="申请人">{{employee.name}}[{{employee.title}}]
</el-descriptions-item>
<el-descriptions-item label="请假类型">
<el-select v-model="form.formType" style="width: 100%">
<el-option label="事假" value="1"></el-option>
<el-option label="病假" value="2"></el-option>
<el-option label="工伤假" value="3"></el-option>
<el-option label="婚嫁" value="4"></el-option>
<el-option label="产假" value="5"></el-option>
<el-option label="丧假" value="6"></el-option>
</el-select>
</el-descriptions-item>
<el-descriptions-item label="请假时间">
<el-form-item prop="timeRange" label-width="0px">
<div class="block">
<el-date-picker
v-model="form.timeRange"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="changeTimeRange">
</el-date-picker>
</div>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="请假原因">
<el-form-item prop="reason" label-width="0px">
<el-input type="text" placeholder="请输入请假原因" v-model="form.reason"/>
</el-form-item>
</el-descriptions-item>
</el-descriptions>
<div style="text-align: center;padding-top: 30px">
<el-button type="primary" v-on:click="onSubmit('leaveForm')" >立即申请</el-button>
</div>
</el-form>
</div>
<script>
var Main = {
data() {
return {
employee:{},
department:{},
form: {
formType: "1",
timeRange: "",
startTime: "",
endTime: "",
reason: "",
eid: ""
},
// 表单验证,需要在 el-form-item 元素中增加 prop 属性
rules: {
timeRange: [
{required: true, message: '请选择请假时间', trigger: 'blur'}
],
reason: [
{required: true, message: '请填写请假原因', trigger: 'blur'}
]
}
}
}
,methods:{
changeTimeRange : function(){
console.info(this.form.timeRange);
this.form.startTime = this.form.timeRange[0].getTime();
this.form.endTime = this.form.timeRange[1].getTime();
}
,onSubmit(formName){
const objApp = this;
const formData = this.form;
const $message = this.$message;
this.$refs[formName].validate(function(valid){
if(valid){
const params = new URLSearchParams();
params.append("formType", formData.formType);
params.append("startTime", formData.startTime);
params.append("endTime", formData.endTime);
params.append("reason", formData.reason);
params.append("eid", sessionStorage.eid);
axios.post("/api/leave/create",params)
.then(function(response){
console.info(response);
const json = response.data;
if(json.code == "0"){
objApp.$alert("请假单已提交,等待上级审批",{
callback:function(){
window.location.href = "/notice.html";
}
})
}else{
$message.error({message:json.message,offset:100})
}
})
}
})
}
}
,mounted(){
const objApp = this;
axios.get("/api/user_info?uid=" + sessionStorage.uid + "&eid=" + sessionStorage.eid)
.then(function(response){
console.info(response);
objApp.employee = response.data.data.employee;
objApp.department = response.data.data.department;
})
}
};
ElementPlus.locale(ElementPlus.lang.zhCn);
const app = Vue.createApp(Main);
app.use(ElementPlus, ElementPlus.lang.zhCn);
app.mount("#app");
</script>
</body>
</html>
请假审核页面
audit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 引入样式 -->
<link rel="stylesheet" type="text/css" href="/assets/element-plus/index.css">
<!-- 引入组件库 -->
<script src="/assets/vue/vue.global.js"></script>
<script src="/assets/element-plus/index.full.js"></script>
<script src="/assets/axios/axios.js"></script>
<style >
.info .el-col,.info .el-select ,.info .el-input{
padding-top: 5px;
padding-bottom: 5px;
}
</style>
</head>
<body>
<div id="app">
<h2>请假审批</h2>
<el-table
ref="singleTable"
:data="tableData"
highlight-current-row
@current-change="handleCurrentChange"
style="width: 100%">
<el-table-column
type="index"
width="50">
</el-table-column>
<el-table-column
property="ctime"
label="申请时间"
width="180">
</el-table-column>
<el-table-column
property="ftype"
label="类型"
width="120">
</el-table-column>
<el-table-column
property="department_name"
label="部门"
width="120">
</el-table-column>
<el-table-column
property="name"
label="员工"
width="120">
</el-table-column>
<el-table-column
property="stime"
label="起始时间"
width="180">
</el-table-column>
<el-table-column
property="etime"
label="结束时间"
width="180">
</el-table-column>
<el-table-column
property="reason"
label="请假原因">
</el-table-column>
</el-table>
<el-dialog title="请假审批" v-model="dialogFormVisible" width="500px" center>
<el-descriptions :column="2" border>
<el-descriptions-item label="部门">{{currentRow.department_name}}</el-descriptions-item>
<el-descriptions-item label="姓名">{{currentRow.name}}</el-descriptions-item>
<el-descriptions-item label="起始时间" >{{currentRow.stime}}</el-descriptions-item>
<el-descriptions-item label="结束时间" >{{currentRow.etime}}</el-descriptions-item>
<el-descriptions-item label="请假原因" :span="2">
{{currentRow.reason}}
</el-descriptions-item>
</el-descriptions>
<div class="info" >
<el-form :model="form" ref="auditForm">
<el-select v-model="form.result" placeholder="是否同意" style="width: 100%">
<el-option label="同意" value="approved"></el-option>
<el-option label="驳回" value="refused"></el-option>
</el-select>
<el-input v-model="form.reason" placeholder="请输入审批意见" autocomplete="off"></el-input>
</el-form>
<span class="dialog-footer">
<el-button type="primary" v-on:click="onSubmit('auditForm')" style="width: 100%">确认提交</el-button>
</span>
</div>
</el-dialog>
</div>
<script>
function formatDate(time){
var newDate = new Date(time);
return newDate.getFullYear() + "-" +
(newDate.getMonth() + 1) + "-" + newDate.getDate()
+ " " + newDate.getHours() + "时";
}
var Main = {
data() {
return {
dialogFormVisible: false,
form: {
result:"approved",
reason:""
},
formLabelWidth: '120px',
tableData: [{
ctime:"2021-5-29 18时",
ftype:"事假",
stime:"2021-5-31 0时",
etime:"2021-6-3 0时",
department_name:"研发部",
name:"王美美",
reason:"测试数据"
}],
currentRow: null
}
}
,methods: {
handleCurrentChange(val) {
this.currentRow = val;
console.info(val);
this.dialogFormVisible = true;
}
,onSubmit(formName){
const objApp = this;
this.$refs[formName].validate(function(valid){
if(valid){
const params = new URLSearchParams();
params.append("formId", objApp.currentRow.form_id);
params.append("result", objApp.form.result);
params.append("reason", objApp.form.reason);
params.append("eid", sessionStorage.eid);
axios.post("/api/leave/audit" , params)
.then(function(response){
const json = response.data;
console.info(json);
if(json.code=="0"){
objApp.$alert("请假已审批完毕" , {
callback:function(){
window.location.href = "/notice.html";
}
})
}else{
objApp.$message.error({message:json.message,offset:100})
}
})
}
})
}
}
,mounted(){
const objApp = this;
const $message = this.$message;
axios.get("/api/leave/list?eid=" + sessionStorage.eid)
.then(function(response){
const json = response.data;
if(json.code == '0'){
objApp.tableData.splice(0, objApp.tableData.length);
const formList = json.data.list;
formList.forEach(function(item){
switch (item.form_type){
case 1:
item.ftype = "事假";
break;
case 2:
item.ftype = "病假";
break;
case 3:
item.ftype = "工伤假";
break;
case 4:
item.ftype = "婚假";
break;
case 5:
item.ftype = "产假";
break;
case 6:
item.ftype = "丧假";
break;
}
item.stime = formatDate(item.start_time);
item.etime = formatDate(item.end_time);
item.ctime = formatDate(item.create_time);
objApp.tableData.push(item);
})
}else{
$message.error({message:json.message,offset:100})
}
})
}
};
const app = Vue.createApp(Main);
app.use(ElementPlus);
app.mount("#app")
</script>
</body>
</html>
消息页面
notice.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>系统通知</title>
<!-- 引入样式 -->
<link rel="stylesheet" type="text/css" href="/assets/element-plus/index.css">
<!-- 引入组件库 -->
<script src="/assets/vue/vue.global.js"></script>
<script src="/assets/element-plus/index.full.js"></script>
<script src="/assets/axios/axios.js"></script>
<script src="/assets/oa/security.js"></script>
</head>
<body>
<div id="app">
<h2>系统通知</h2>
<el-table
ref="singleTable"
:data="tableData"
highlight-current-row
style="width: 100%">
<el-table-column
property="index"
label="序号"
width="50">
</el-table-column>
<el-table-column
property="ctime"
label="通知时间"
width="180">
</el-table-column>
<el-table-column
property="content"
label="通知内容">
</el-table-column>
</el-table>
</div>
<script>
var Main = {
data() {
return {
tableData: []
}
}
,mounted() {
const objApp = this;
axios.get("/api/notice/list?eid=" + sessionStorage.eid)
.then(function (response) {
objApp.tableData.splice(0, objApp.tableData.length);
response.data.data.list.forEach(function (item,index) {
var date = new Date(item.createTime);
item.ctime = date.getFullYear() + "-" +
(date.getMonth() + 1) + "-" + date.getDate()
+ " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
item.index = index + 1;
objApp.tableData.push(item);
});
})
.catch(function (error) {
console.log(error);
});
}
};
const app = Vue.createApp(Main);
app.use(ElementPlus);
app.mount("#app")
</script>
</body>
</html>