Files
klp-oa/klp-ui/src/views/wms/hrm/leaveReport.vue
砂糖 f6000018d8 feat(hrm): 新增请假统计报表功能及优化请假申请页面
新增请假统计报表页面,包含趋势图、部门统计图、类型统计图和表格展示
优化请假申请页面布局和字段展示
添加多个图表组件用于数据可视化展示
调整样式和表单验证规则
2026-01-19 13:28:45 +08:00

256 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="leave-statistics-container">
<!-- 1. 时间筛选区域 -->
<div class="search-form">
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
<el-form-item label="请假时间">
<el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始时间"
end-placeholder="结束时间" value-format="yyyy-MM-dd" style="width: 260px"></el-date-picker>
</el-form-item>
<el-form-item label="请假人">
<dict-select v-model="queryParams.applicantName" dict-type="hrm_leave_employee" placeholder="请选择请假人"
style="width: 260px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
<el-button icon="el-icon-download" type="warning" plain @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
</div>
<!-- 2. 图表展示区域 - 横向三个图表组件 -->
<div class="chart-box">
<div class="chart-item">
<leave-trend-chart :chart-data="trendChartData" />
</div>
<div class="chart-item">
<leave-dept-chart :chart-data="deptChartData" />
</div>
<div class="chart-item">
<leave-type-chart :chart-data="typeChartData" />
</div>
</div>
<!-- 3. 请假记录表格区域 - 封装的折叠表格组件 -->
<div class="table-box">
<leave-record-table :table-data="tableGroupData" :loading="loading" />
</div>
</div>
</template>
<script>
import { listLeaveRequest } from "@/api/wms/leaveRequest";
// 引入4个封装的子组件
import LeaveTrendChart from './components/LeaveTrendChart';
import LeaveDeptChart from './components/LeaveDeptChart';
import LeaveTypeChart from './components/LeaveTypeChart';
import LeaveRecordTable from './components/LeaveRecordTable';
import DictSelect from '@/components/DictSelect';
export default {
name: 'LeaveStatistics',
components: {
LeaveTrendChart,
LeaveDeptChart,
LeaveTypeChart,
LeaveRecordTable,
DictSelect
},
data() {
return {
loading: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 9999, // 查全部数据用于图表统计
startTime: '',
endTime: '',
},
leaveIds: '', // 请假人ID列表
dateRange: [], // 时间筛选绑定值
leaveRequestList: [], // 原始请假数据列表
// 图表渲染数据
trendChartData: { xAxis: [], countData: [], dayData: [] }, // 折线图数据
deptChartData: { xAxis: [], countData: [], dayData: [] }, // 柱状图数据
typeChartData: { countPie: [], dayPie: [] }, // 饼图数据
tableGroupData: [] // 表格分组数据(按请假人聚合)
};
},
mounted() {
// 初始化默认时间:当月第一天 至 当前时间
this.initDefaultTime();
// 加载数据
this.getList();
},
methods: {
// 初始化默认时间 - 当月1号 00:00:00 至 当前时间 yyyy-MM-dd HH:mm:ss
initDefaultTime() {
const now = new Date();
// 当月第一天
const monthFirst = new Date(now.getFullYear(), now.getMonth(), 1);
const startTime = this.formatDateTime(monthFirst);
const endTime = this.formatDateTime(now);
this.dateRange = [startTime, endTime];
// this.queryParams.startTime = startTime;
// this.queryParams.endTime = endTime;
},
// 时间格式化yyyy-MM-dd HH:mm:ss
formatDateTime(date) {
const y = date.getFullYear();
const m = (date.getMonth() + 1).toString().padStart(2, '0');
const d = date.getDate().toString().padStart(2, '0');
const h = date.getHours().toString().padStart(2, '0');
const mm = date.getMinutes().toString().padStart(2, '0');
const s = date.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d}`;
},
// 查询按钮事件
handleQuery() {
if (this.dateRange && this.dateRange.length === 2) {
// this.queryParams.startTime = this.dateRange[0];
// this.queryParams.endTime = this.dateRange[1];
}
this.getList();
},
// 重置按钮事件
resetQuery() {
this.dateRange = [];
this.initDefaultTime();
this.getList();
},
// 调取接口获取数据
getList() {
this.loading = true;
listLeaveRequest(this.queryParams).then(res => {
this.leaveRequestList = res.rows || [];
// 提取所有请假人ID
this.leaveIds = this.leaveRequestList.map(item => item.leaveId).join(',');
// 核心:数据处理,为图表和表格格式化数据
this.formatAllData();
this.loading = false;
}).catch(() => {
this.loading = false;
});
},
handleExport() {
// 导出逻辑
if (!this.leaveIds) {
this.$message.warning('请先查询数据');
return;
}
this.download('/wms/leaveRequest/export', {
leaveIds: this.leaveIds
}, '请假记录.xlsx');
},
// 格式化所有图表+表格数据
formatAllData() {
const list = this.leaveRequestList;
// ========== 1. 处理【按时间汇总】折线图数据 ==========
const timeObj = {};
list.forEach(item => {
const day = item.startTime?.split(' ')[0]; // 按日期聚合
if (!day) return;
if (!timeObj[day]) {
timeObj[day] = { count: 0, days: 0 };
}
timeObj[day].count += 1; // 记录数+1
timeObj[day].days += Number(item.leaveDays) || 0; // 请假天数累加
});
this.trendChartData.xAxis = Object.keys(timeObj).sort(); // 日期排序
this.trendChartData.countData = this.trendChartData.xAxis.map(k => timeObj[k].count);
this.trendChartData.dayData = this.trendChartData.xAxis.map(k => timeObj[k].days);
// ========== 2. 处理【按部门汇总】柱状图数据 ==========
const deptObj = {};
list.forEach(item => {
const dept = item.applicantDeptName || '未知部门';
if (!deptObj[dept]) {
deptObj[dept] = { count: 0, days: 0 };
}
deptObj[dept].count += 1;
deptObj[dept].days += Number(item.leaveDays) || 0;
});
this.deptChartData.xAxis = Object.keys(deptObj);
this.deptChartData.countData = this.deptChartData.xAxis.map(k => deptObj[k].count);
this.deptChartData.dayData = this.deptChartData.xAxis.map(k => deptObj[k].days);
// ========== 3. 处理【按请假类型汇总】饼图数据 ==========
const typeObj = {};
list.forEach(item => {
const type = item.leaveType || '未知类型';
if (!typeObj[type]) {
typeObj[type] = { count: 0, days: 0 };
}
typeObj[type].count += 1;
typeObj[type].days += Number(item.leaveDays) || 0;
});
this.typeChartData.countPie = Object.keys(typeObj).map(k => ({ name: k, value: typeObj[k].count }));
this.typeChartData.dayPie = Object.keys(typeObj).map(k => ({ name: k, value: typeObj[k].days }));
console.log(this.tableGroupData, '按请假人分组表格数据');
// ========== 4. 处理【按请假人分组】表格折叠数据 ==========
const userObj = {};
list.forEach(item => {
const userName = item.applicantName || '未知人员';
if (!userObj[userName]) {
userObj[userName] = [];
}
userObj[userName].push({
...item,
leaveTime: `${item.startTime} ~ ${item.endTime}`, // 拼接请假时间段
leaveDays: parseInt(item.leaveDays || 0) // 转换为整数
});
});
this.tableGroupData = Object.keys(userObj).map(user => ({
leaveId: user,
userName: user,
leaveCount: userObj[user].length || 0, // 请假次数
leaveDays: parseInt(userObj[user].reduce((sum, item) => sum + item.leaveDays, 0)), // 请假总时长(整数)
children: userObj[user] // 折叠子数据
}));
console.log(this.tableGroupData, '按请假人分组表格数据');
}
}
};
</script>
<style scoped lang="scss">
.leave-statistics-container {
padding: 20px;
box-sizing: border-box;
}
.search-form {
margin-bottom: 20px;
}
.chart-box {
display: flex;
gap: 15px;
margin-bottom: 20px;
height: 320px;
.chart-item {
flex: 1;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 10px;
box-sizing: border-box;
}
}
.table-box {
width: 100%;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 10px;
box-sizing: border-box;
}
</style>