rollback^2
lion 8 months ago
parent 36d990e4f1
commit 31822655f5

@ -20,6 +20,7 @@
"echarts": "^5.0.0",
"element-ui": "^2.15.14",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"js-cookie": "2.2.0",
"json-bigint": "^1.0.0",
"moment": "^2.29.4",
@ -33,7 +34,8 @@
"vuex": "3.1.0",
"vxe-pc-ui": "^3.1.0",
"vxe-table": "^3.8.25",
"vxe-table-plugin-export-xlsx": "^3.3.4"
"vxe-table-plugin-export-xlsx": "^3.3.4",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.4",

@ -2,7 +2,7 @@
<div id="app">
<router-view />
<ThemePicker style="display: none;"></ThemePicker>
<!-- <ThemePicker style="display: none;"></ThemePicker> -->
<OnlineFile></OnlineFile>
</div>
</template>

@ -0,0 +1,19 @@
import request from '@/utils/request'
export function leaveList(params, isLoading = true) {
return request({
method: 'get',
url: '/api/oa/chart/leave',
params,
isLoading
})
}
export function overtimeList(params,isLoading = true) {
return request({
method: 'get',
url: '/api/oa/chart/overtime',
params,
isLoading
})
}

@ -110,4 +110,3 @@ if (window.__POWERED_BY_WUJIE__) {
}
}).$mount("#app")
}

@ -0,0 +1,174 @@
<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="{}" :print-config="{}" :column-config="{ resizable: true }"
:expand-config="{
visibleMethod: () => false,
trigger: 'manual'
}" :data="tableData" :span-method="mergeCells">
<vxe-column width="240" header-align="center" align="center" field="department_name" title="科室"></vxe-column>
<vxe-column width="180" header-align="center" align="center" field="user.name" title="姓名"></vxe-column>
<vxe-column header-align="center" align="center" field="over_off" title="结余调休时间">
<template #default="{ row }">
<div>
{{row.over_off=='0.00'?'0':row.over_off}}
</div>
</template>
</vxe-column>
<vxe-column header-align="center" align="center" field="overtime" title="本月加班时间">
<template #default="{ row }">
<div>
{{row.overtime=='0.00'?'0':row.overtime}}
</div>
</template>
</vxe-column>
<vxe-column header-align="center" align="center" field="time_off" title="本月调休时间">
<template #default="{ row }">
<div>
{{row.time_off=='0.00'?'0':row.time_off}}
</div>
</template>
</vxe-column>
<vxe-column header-align="center" align="center" field="expire" title="过期天数">
<template #default="{ row }">
<div>
{{row.expire=='0.00'?'0':row.expire}}
</div>
</template>
</vxe-column>
<vxe-column header-align="center" align="center" field="has_time_off" title="剩余调休时间">
<template #default="{ row }">
<div>
{{row.has_time_off=='0.00'?'0':row.has_time_off}}
</div>
</template>
</vxe-column>
</vxe-table>
</card-container>
</div>
</template>
<script>
import {
leaveList
} from "@/api/chart"
import {
departmentListNoAuth
} from "@/api/common.js"
export default {
data() {
return {
loading: false,
tableData: [],
select: {
page: 1,
page_size: 10,
month: this.$moment().format('YYYY-MM'),
department_id: ''
},
departments:[]
}
},
methods: {
async getList() {
this.loading = true;
try {
const res = await leaveList(this.select, false);
console.log(res);
let _arr = res?.timeOff || []
_arr.forEach(item=>{
item.department_name = item.user.department.name
})
this.tableData = _arr;
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);
}
},
mergeCells({
row,
$rowIndex,
column,
data
}) {
console.log("row, $rowIndex, column, data", {
row,
$rowIndex,
column,
data
})
let fields = ["department_name"]
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
}
}
}
}
}
},
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>

@ -0,0 +1,294 @@
<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;
// icountwhile
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>
Loading…
Cancel
Save