|
|
|
|
|
<template>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<card-container>
|
|
|
|
|
|
<vxe-toolbar print custom export>
|
|
|
|
|
|
<template #buttons>
|
|
|
|
|
|
<el-date-picker v-model="select.month" type="month" size="small" value-format="yyyy-MM" placeholder="月份"
|
|
|
|
|
|
format="yyyy-MM" />
|
|
|
|
|
|
<el-select style="width:250px;margin-left:6px" size="small" v-model="select.department_id" placeholder="请选择">
|
|
|
|
|
|
<el-option v-for="item in departments" :key="item.id" :label="item.name" :value="item.id">
|
|
|
|
|
|
</el-option>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
<el-button icon="el-icon-search" type="primary" plain size="small" style="margin-left: 6px;"
|
|
|
|
|
|
@click="getList">搜索</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</vxe-toolbar>
|
|
|
|
|
|
<vxe-table ref="table" stripe :border='true' style="margin-top: 10px;" :loading="loading" :max-height="1400"
|
|
|
|
|
|
:min-height="400" :export-config="{type: 'xlsx'}" :print-config="{}" :column-config="{ resizable: true }"
|
|
|
|
|
|
:expand-config="{
|
|
|
|
|
|
visibleMethod: () => false,
|
|
|
|
|
|
trigger: 'manual'
|
|
|
|
|
|
}" :data="tableData" :span-method="spanMethods">
|
|
|
|
|
|
<!-- :span-method="spanMethods" :merge-cells="mergeCells"-->
|
|
|
|
|
|
<vxe-column width="180" header-align="center" align="center" field="department_name" title="科室"></vxe-column>
|
|
|
|
|
|
<vxe-column width="180" header-align="center" align="center" field="name" title="姓名"></vxe-column>
|
|
|
|
|
|
<vxe-column width="180" header-align="center" align="center" field="kaishiriqi" title="日期">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{row.kaishiriqi?row.kaishiriqi.substring(0,11):''}}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</vxe-column>
|
|
|
|
|
|
<vxe-column header-align="center" align="center" field="yuanyinshuoming" title="加班事由"></vxe-column>
|
|
|
|
|
|
<vxe-column :export-method="exportjbMethod" header-align="center" align="center" field="jiabanshijian"
|
|
|
|
|
|
title="加班时间">
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
{{row.kaishiriqi?row.kaishiriqi.substring(11,row.kaishiriqi.length):''}}-{{row.kaishiriqi?row.jieshushijian.substring(11,row.jieshushijian.length):''}}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</vxe-column>
|
|
|
|
|
|
<vxe-column header-align="center" align="center" field="jiabanshichang" title="加班时长(h)"></vxe-column>
|
|
|
|
|
|
<!-- <vxe-column header-align="center" align="center" field="day" title="day"></vxe-column> -->
|
|
|
|
|
|
<vxe-column header-align="center" align="center" field="allDay" title="本月累计(d)"></vxe-column>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</vxe-table>
|
|
|
|
|
|
</card-container>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import {
|
|
|
|
|
|
overtimeList
|
|
|
|
|
|
} from "@/api/chart"
|
|
|
|
|
|
import {
|
|
|
|
|
|
departmentListNoAuth
|
|
|
|
|
|
} from "@/api/common.js"
|
|
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
|
|
import {
|
|
|
|
|
|
saveAs
|
|
|
|
|
|
} from "file-saver";
|
|
|
|
|
|
export default {
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
loading: false,
|
|
|
|
|
|
tableData: [],
|
|
|
|
|
|
select: {
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
page_size: 10,
|
|
|
|
|
|
month: this.$moment().format('YYYY-MM'),
|
|
|
|
|
|
department_id: ''
|
|
|
|
|
|
},
|
|
|
|
|
|
departments: [],
|
|
|
|
|
|
mergeCells: [
|
|
|
|
|
|
// { row: 0, col: 2, rowspan: 1, colspan: 2 },
|
|
|
|
|
|
// { row: 2, col: 2, rowspan: 2, colspan: 1 }
|
|
|
|
|
|
],
|
|
|
|
|
|
columns: [{
|
|
|
|
|
|
field: 'department_name',
|
|
|
|
|
|
title: '科室'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'name',
|
|
|
|
|
|
title: '姓名'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'kaishiriqi',
|
|
|
|
|
|
title: '开始日期'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'yuanyinshuoming',
|
|
|
|
|
|
title: '加班事由'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'jiabanshijian',
|
|
|
|
|
|
title: '加班时间'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'yuanyinshuoming',
|
|
|
|
|
|
title: '加班事由'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'jiabanshichang',
|
|
|
|
|
|
title: '加班时长(h)'
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
field: 'allDay',
|
|
|
|
|
|
title: '本月累计(d)'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
async getList() {
|
|
|
|
|
|
this.loading = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await overtimeList(this.select, false);
|
|
|
|
|
|
console.log(res);
|
|
|
|
|
|
let data = res?.users || [];
|
|
|
|
|
|
this.tableData = this.overtimeData(data)
|
|
|
|
|
|
console.log("this.tableData", this.tableData)
|
|
|
|
|
|
// this.setRowSpans()
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error(err);
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async getDepartmentList() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await departmentListNoAuth();
|
|
|
|
|
|
console.log(res);
|
|
|
|
|
|
let arr = res
|
|
|
|
|
|
this.departments = arr
|
|
|
|
|
|
this.departments.unshift({
|
|
|
|
|
|
id: '',
|
|
|
|
|
|
name: '全部'
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error(err);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
overtimeData(arr) {
|
|
|
|
|
|
let _arr = []
|
|
|
|
|
|
const result = [];
|
|
|
|
|
|
_arr = arr.filter(item => item.overtime.length > 0)
|
|
|
|
|
|
|
|
|
|
|
|
_arr.forEach(item => {
|
|
|
|
|
|
const {
|
|
|
|
|
|
id,
|
|
|
|
|
|
name,
|
|
|
|
|
|
department,
|
|
|
|
|
|
overtime
|
|
|
|
|
|
} = item;
|
|
|
|
|
|
const allDay = overtime.reduce((sum, overtimeItem) => sum + parseFloat(overtimeItem.day), 0);
|
|
|
|
|
|
|
|
|
|
|
|
overtime.forEach(overtimeItem => {
|
|
|
|
|
|
overtimeItem.pid = id;
|
|
|
|
|
|
overtimeItem.name = name;
|
|
|
|
|
|
overtimeItem.department_name = department.name
|
|
|
|
|
|
overtimeItem.allDay = allDay;
|
|
|
|
|
|
result.push(overtimeItem);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
return result;
|
|
|
|
|
|
},
|
|
|
|
|
|
exportjbMethod({
|
|
|
|
|
|
row
|
|
|
|
|
|
}) {
|
|
|
|
|
|
return `${row.kaishiriqi?row.kaishiriqi.substring(11,row.kaishiriqi.length):''}-${row.kaishiriqi?row.jieshushijian.substring(11,row.jieshushijian.length):''}`
|
|
|
|
|
|
},
|
|
|
|
|
|
spanMethods({
|
|
|
|
|
|
row,
|
|
|
|
|
|
$rowIndex,
|
|
|
|
|
|
column,
|
|
|
|
|
|
data
|
|
|
|
|
|
}) {
|
|
|
|
|
|
let fields = ["department_name", "name", "allDay"]
|
|
|
|
|
|
let cellValue = row[column.property]
|
|
|
|
|
|
if (cellValue && fields.includes(column.property)) {
|
|
|
|
|
|
let prevRow = data[$rowIndex - 1]
|
|
|
|
|
|
let nextRow = data[$rowIndex + 1]
|
|
|
|
|
|
if (prevRow && prevRow[column.property] === cellValue) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
rowspan: 0,
|
|
|
|
|
|
colspan: 0
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let countRowspan = 1
|
|
|
|
|
|
while (nextRow && nextRow[column.property] === cellValue) {
|
|
|
|
|
|
nextRow = data[++countRowspan + $rowIndex]
|
|
|
|
|
|
}
|
|
|
|
|
|
if (countRowspan > 1) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
rowspan: countRowspan,
|
|
|
|
|
|
colspan: 1
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
// 合并单元格
|
|
|
|
|
|
setRowSpans() {
|
|
|
|
|
|
const options = {
|
|
|
|
|
|
list: this.tableData, // 表格数据
|
|
|
|
|
|
columns: this.columns, // 完整表头
|
|
|
|
|
|
merges: [ "name", "department_name"], // 需要合并的列
|
|
|
|
|
|
condition: ['pid'], // 合并条件
|
|
|
|
|
|
field: 'field' // 表头取值的字段
|
|
|
|
|
|
};
|
|
|
|
|
|
// 传入此配置项,是指将pid相等的相邻数据合并,如果两条数据不相邻即使pid相等也不会合并
|
|
|
|
|
|
this.mergeCells = this.setMergeCells(options);
|
|
|
|
|
|
console.log('this.mergeCells: ', this.mergeCells);
|
|
|
|
|
|
},
|
|
|
|
|
|
setMergeCells({
|
|
|
|
|
|
list = [],
|
|
|
|
|
|
columns = [],
|
|
|
|
|
|
merges = [],
|
|
|
|
|
|
condition = [],
|
|
|
|
|
|
field = 'field'
|
|
|
|
|
|
}) {
|
|
|
|
|
|
console.log("list",list)
|
|
|
|
|
|
const validMap = new Map(); // 有效合并配置项
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
// 根据判断条件取出当前行对应的表格数据,生成唯一标识
|
|
|
|
|
|
console.log("list[i][field])",i,list[i]['pid'])
|
|
|
|
|
|
const hashKey = condition.map(field => list[i][field]).join('&&');
|
|
|
|
|
|
let flag = true;
|
|
|
|
|
|
let count = 1;
|
|
|
|
|
|
// 从当前索引i开始,往后找,数据相同的话,count自增,否则跳出while循环
|
|
|
|
|
|
while (flag && i + count < list.length) {
|
|
|
|
|
|
// 根据判断条件取出下一行的表格数据,生成唯一标识
|
|
|
|
|
|
const nextRow = condition.map(field => list[i + count][field]).join('&&');
|
|
|
|
|
|
if (hashKey === nextRow) {
|
|
|
|
|
|
// 数据相同,count自增
|
|
|
|
|
|
count++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 数据不同,跳出while循环
|
|
|
|
|
|
flag = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 将有效的合并配置项存入validMap
|
|
|
|
|
|
count > 1 && validMap.set(hashKey + '_' + i, {
|
|
|
|
|
|
row: i,
|
|
|
|
|
|
rowspan: count
|
|
|
|
|
|
});
|
|
|
|
|
|
// i 跳过已合并的行,减 1 是因为 i++
|
|
|
|
|
|
i += count - 1;
|
|
|
|
|
|
// 重置 while 循环标识
|
|
|
|
|
|
flag = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 合并记录转换
|
|
|
|
|
|
const recordsList = [...validMap.values()];
|
|
|
|
|
|
console.log("recordsList",recordsList)
|
|
|
|
|
|
if (recordsList.length == 0) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 预处理列索引
|
|
|
|
|
|
const cols = merges.reduce((acc, mergeField) => {
|
|
|
|
|
|
const idx = columns.findIndex(item => item[field] === mergeField);
|
|
|
|
|
|
idx !== -1 && acc.push(idx);
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
console.log("cols",cols)
|
|
|
|
|
|
// 遍历列索引,生成合并单元格配置集合
|
|
|
|
|
|
const mergeCells = recordsList.flatMap(({
|
|
|
|
|
|
row,
|
|
|
|
|
|
rowspan
|
|
|
|
|
|
}) => cols.map(col => ({
|
|
|
|
|
|
row,
|
|
|
|
|
|
col,
|
|
|
|
|
|
rowspan,
|
|
|
|
|
|
colspan: 1
|
|
|
|
|
|
})));
|
|
|
|
|
|
return mergeCells;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {},
|
|
|
|
|
|
created() {
|
|
|
|
|
|
this.getDepartmentList()
|
|
|
|
|
|
this.getList()
|
|
|
|
|
|
},
|
|
|
|
|
|
mounted() {
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
if (this.$refs["table"] && this.$refs["toolbar"]) {
|
|
|
|
|
|
this.$refs["table"].connect(this.$refs["toolbar"]);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
</style>
|