123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- <template>
- <div class="attendanceCenterDep">
- <h1>考勤统计表</h1>
- <div class="depSearch">
- <div class="depSearchFlex">
- <div class="searBox">
- <span class="span">月份:</span>
- <el-date-picker v-model="fromParams.month" type="month" placeholder="请选择月份" />
- </div>
- <div class="searBox">
- <span class="span">部门:</span>
- <DeptTree
- v-model="fromParams['deptId']"
- placeholder="请选择部门"
- :disabled="porps.deptsName !== '公司'"
- check-strictly
- />
- </div>
- <div class="searBox">
- <span class="span">人员:</span>
- <el-input v-model="fromParams.userName" placeholder="请输入人员名称" />
- </div>
- <div class="searBtns">
- <el-button @click="querysClick" type="primary" style="background: #3485ff">
- <img src="@/assets/imgs/OA/search.png" class="mr-8px" alt="" />
- 查询</el-button
- >
- <ExportToExcel
- v-if="porps.deptsName == '公司'"
- :data="excelDataSource"
- :file-name="`${moment(fromParams.month).format('YYYY年MM月')}考勤统计.xlsx`"
- :colsWidth="colsWidth"
- :title="`${moment(fromParams.month).format('YYYY年MM月')}-考勤数据表`"
- />
- <el-button
- v-if="porps.deptsName == '公司'"
- type="warning"
- plain
- @click="handleImport"
- v-hasPermi="['adm:attendance-sheet:import']"
- >
- <Icon icon="ep:upload" /> 导入
- </el-button>
- <!-- <el-button v-if="porps.deptsName == '公司'" plain @click="dialogVisible = true">
- 免考勤人员设置
- </el-button> -->
- <el-button type="success" style="background-color: #05ce9e" v-if="false">
- <img src="@/assets/imgs/OA/kq/kqqrIcon.png" class="mr-8px" alt="" />
- 考勤确认</el-button
- >
- </div>
- </div>
- <!-- 免考勤人员设置 -->
- <el-dialog v-model="dialogVisible" title="免考勤人员设置" width="500" align-center>
- <UserOrgTree v-model="notAttendanceList" :multiple="true" placeholder="请选择免考勤人员" />
- <template #footer>
- <div class="dialog-footer">
- <el-button @click="dialogVisible = false">关闭</el-button>
- <el-button type="primary" @click="noAttendanceSet"> 确定 </el-button>
- </div>
- </template>
- </el-dialog>
- <el-popover :width="50">
- <template #reference>
- <div class="depStateBox">
- <p>?</p>
- </div>
- </template>
- <template #default>
- <div class="depStateContent">
- <ul class="">
- <li class="flex" v-for="(l, i) in depStateContentList" :key="i">
- <div v-if="i == 0" class="flex">
- <img src="@/assets/imgs/OA/kq/gouzi.png" alt="" />:
- <p>{{ l.name }}</p>
- </div>
- <div v-else class="flex">
- <h4>{{ l.deps }}:</h4>
- <p>{{ l.name }}</p>
- </div>
- </li>
- </ul>
- </div>
- </template>
- </el-popover>
- </div>
- <div class="depTable">
- <el-table
- v-loading="tableLoading"
- :data="
- tableData.slice(
- (pagesList.pageNo - 1) * pagesList.pageSize,
- pagesList.pageSize * pagesList.pageNo
- )
- "
- style="width: 100%; height: 100%"
- stripe
- >
- <!-- <el-table-column type="index" width="50" /> -->
- <el-table-column prop="deptName" label="部门" />
- <el-table-column prop="nickName" label="姓名" width="80" />
- <el-table-column
- v-for="(item, index) in tableHeadList"
- :key="index"
- :label="item.dayOfWeek"
- width="30"
- :label-class-name="item.isworkday == 1 ? '' : 'workday'"
- >
- <el-table-column
- :label-class-name="item.isworkday == 1 ? '' : 'workday'"
- :label="item.day"
- width="30"
- >
- <template #default="scope">
- <div class="iconsFlex" v-if="item.isworkday == 1">
- <div class="sw" @click="swStateClick(scope.row, item, 1)">
- <div v-if="sco(scope.row, item, 1) == '1'">
- <img src="@/assets/imgs/OA/kq/gouzi.png" alt="" />
- </div>
- <div v-else class="spans">
- {{ attendanceTypeObj[sco(scope.row, item, 1)] ?? '' }}
- </div>
- </div>
- <div class="xw" @click="swStateClick(scope.row, item, 2)">
- <div v-if="sco(scope.row, item, 2) == '1'">
- <img src="@/assets/imgs/OA/kq/gouzi.png" alt="" />
- </div>
- <div v-else class="spans">
- {{ attendanceTypeObj[sco(scope.row, item, 2)] ?? '' }}
- </div>
- </div>
- </div>
- </template>
- </el-table-column>
- </el-table-column>
- <el-table-column
- v-for="(item, index) in columList"
- :prop="item.prop"
- width="40"
- label-class-name="labelModeRl"
- :key="index"
- >
- <template #header>
- <span>{{ item.text }}</span>
- <span class="m-t-8px font-size-14px c-#989FA8">{{ item.unit }}</span>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <div class="depPages">
- <el-pagination
- :current-page="pagesList.pageNo"
- :page-size="pagesList.pageSize"
- background
- layout="total, prev, pager, next, jumper"
- :total="pagesList.total"
- @current-change="handleCurrentChange"
- />
- </div>
- <DepsUpdataDetail ref="depsDetailRef" @updataInit="updataInit" />
- </div>
- <!-- 用户导入对话框 -->
- <AttendanceSheetImportForm ref="importFormRef" @success="init" />
- </template>
- <script setup lang="ts">
- defineOptions({ name: 'AttendanceDep' })
- import * as DeptApi from '@/api/system/dept'
- import DepsUpdataDetail from './components/depsUpdataDetail.vue'
- import { handleTree } from '@/utils/tree'
- import * as MineApi from '@/api/oa/attendanceCenter'
- import { arrFlatten, depSort, isArrayDelOrNickname, allDeptsArr, dayOfWeekCall } from './attendAuth'
- import moment from 'moment'
- import { depsState } from './depsState'
- import ExportToExcel from '@/components/ExportToExcel/index.vue'
- import { getUserInfo } from '@/utils/tool'
- import AttendanceSheetImportForm from './components/attendanceSheetImportForm.vue'
- import DeptTree from '@/views/OaSystem/components/DeptTree/index.vue'
- import UserOrgTree from '@/views/OaSystem/components/UserOrgTree/index.vue'
- const userInfo = getUserInfo()
- const porps = defineProps(['deptsName'])
- const pagesList: any = ref({
- pageNo: 1,
- pageSize: 12,
- total: 0
- })
- const fromParams: any = ref({
- deptId: userInfo.deptId ?? '',
- month: '',
- userName: ''
- })
- if (porps.deptsName == '公司') {
- fromParams.value.deptId = '4e99393c-c0ea-4146-a7fb-56fb8019c477'
- }
- const deptSort: any = ref([])
- const deptList = ref<Tree[]>([]) // 树形结构
- const tableData = ref([])
- const initTreeDeps = async () => {
- DeptApi.getSimpleDeptList().then((res) => {
- deptList.value = handleTree(res)
- deptSort.value = arrFlatten(res, 'children')
- })
- }
- const depStateContentList = ref()
- depStateContentList.value = depsState
- const handleCurrentChange = async (page) => {
- pagesList.value.pageNo = page
- let toMoseMonths: any = []
- toMoseMonths[0] = moment().startOf('months').format('YYYY-MM-DD') + ' 00:00:00'
- toMoseMonths[1] = moment().endOf('months').format('YYYY-MM-DD') + ' 23:59:59'
- // initInsMouth(toMoseMonths)
- }
- const querysClick = async () => {
- // initInsMouth()
- pagesList.value.pageNo = 1
- let month = fromParams.value.month
- let toMoseMonths: any = []
- toMoseMonths[0] = moment(month).startOf('months').format('YYYY-MM-DD') + ' 00:00:00'
- toMoseMonths[1] = moment(month).endOf('months').format('YYYY-MM-DD') + ' 23:59:59'
- initInsMouth(toMoseMonths)
- }
- const tableLoading = ref(true)
- const updataInit = (date) => {
- initInsMouth(date)
- }
- const initInsMouth = async (date: any) => {
- tableLoading.value = true
- let params = {
- attendanceDate: date, // 考勤时间
- attendanceStatus: '', // 考勤状态,示例值(2)
- attendanceType: '', // 考勤类型(1:上午;2:下午),示例值(1)
- otherMinute: '', // 其他考勤状态分钟(除了出勤)
- nickname: fromParams.value.userName, //用户昵称
- deptId: fromParams.value.deptId //部门ID
- }
- initWorkDay(date).then((restall) => {
- MineApi.getWorkdaySheetList(params).then((res) => {
- let namesArr: any = isArrayDelOrNickname(JSON.parse(JSON.stringify(res)))
- let resArr: any = res
- resArr.forEach((item: any) => {
- if (item.attendanceTime) {
- item.date = moment(item.attendanceTime).format('YYYY-MM-DD')
- } else {
- item.date = moment(item.attendanceDate).format('YYYY-MM-DD')
- }
- })
- let arr = allDeptsArr(restall, resArr, namesArr)
- let sbArr = depSort(deptSort.value, arr)
- // 导出数据
- excelDataSource.value = getExcelTable(sbArr)
- tableData.value = sbArr
- pagesList.value.total = arr.length
- tableLoading.value = false
- })
- })
- }
- const sco = (row, item, index) => {
- let kq = ''
- if (index == 1) {
- row.attendArray.forEach((k) => {
- if (k.date == item.date) {
- kq = k.swAttendanceStatus ?? ''
- }
- })
- } else {
- row.attendArray.forEach((k) => {
- if (k.date == item.date) {
- kq = k.xwAttendanceStatus ?? ''
- }
- })
- }
- return kq
- }
- const tableHeadList: any = ref([])
- const initWorkDay = async (date: any) => {
- let params = {
- dateDay: date, // 日期(数组)
- dayOfWeek: '', // 星期几
- holidayRemark: '', // 节假备注,示例值(国庆节)
- isworkday: '', // 是否工作日
- year: '', // 年份
- month: '', // 月份
- week: '' // 周
- }
- const res = await MineApi.getWorkdayList(params)
- let arr = res
- arr.forEach((item: any) => {
- item.date = moment(item.dateDay).format('YYYY-MM-DD')
- item.prop = item.dayOfWeek
- item.dayOfWeek = dayOfWeekCall(item.dayOfWeek)
- item.day = moment(item.dateDay).format('DD')
- })
- tableHeadList.value = arr
- return await arr
- }
- const depsDetailRef = ref()
- const swStateClick = (row, item, index) => {
- //更改考勤
- if (porps.deptsName == '公司') {
- depsDetailRef.value.initShow(row, item, index, fromParams.value.month)
- return
- }
- }
- // 考勤对应的字典
- const attendanceTypeObj = {
- '1': '', // 出勤
- '2': '迟', // '迟到'
- '3': '早', // '早退'
- '4': '旷', // '旷工'
- '5': '差', // '出差'
- '11': '事', // '事假'
- '12': '病', // '病假'
- '13': '婚', // '婚假'
- '14': '产', // '产假'
- '15': '陪', // '陪产假'
- '19': '丧', // '丧假'
- '16': '伤', // '工伤'
- '17': '年', // '年假'
- '18': '调', // '调休'
- '20': '假' // '其他'
- }
- // 考勤统计
- const columList = [
- { text: '旷 工', prop: 'kg', unit: '天' },
- { text: '迟 到', prop: 'cd', unit: '次' },
- { text: '早 退', prop: 'zt', unit: '次' },
- { text: '出 差', prop: 'cc', unit: '天' },
- { text: '事 假', prop: 'sj', unit: '天' },
- { text: '调 休', prop: 'tx', unit: '天' },
- { text: '病 假', prop: 'bj', unit: '天' },
- { text: '年 假', prop: 'nj', unit: '天' },
- { text: '产 假', prop: 'cj', unit: '天' },
- { text: '其 他', prop: 'qt', unit: '天' }
- ]
- // 考勤表格
- const excelDataSource = ref<any>()
- // 合并的单元格
- // const mergeRanges = [
- // { s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // 合并第一行的前两列
- // { s: { r: 0, c: 1 }, e: { r: 1, c: 1 } } // 合并第二行的前两列
- // ]
- // 列宽设置
- const colsWidth = ref([{ wch: 30 }, { wch: 10 }, { wch: 8 }])
- // 获取导出的数据表格
- const getExcelTable = (attendanceData) => {
- const tableHead = [['部门', '姓名', '时间', ...tableHeadList.value.map((item) => item.day)]]
- // 日期单元格列宽设置
- colsWidth.value = [...colsWidth.value, ...tableHeadList.value.map(() => ({ wch: 3 }))]
- const tableBody: any = []
- for (let index = 0; index < attendanceData.length; index++) {
- const item = attendanceData[index]
- const data1 = item.attendArray.map((arr, i) => {
- // 判断是不是工作日
- const isworkday = tableHeadList.value[i].isworkday
- if (!isworkday) return ''
- // 判断有没有数据
- if (!arr.swAttendanceStatus) return ''
- return attendanceTypeObj[arr.swAttendanceStatus] ?? ''
- })
- const data2 = item.attendArray.map((arr, i) => {
- const isworkday = tableHeadList.value[i].isworkday
- if (!isworkday) return ''
- if (!arr.swAttendanceStatus) return ''
- return attendanceTypeObj[arr.xwAttendanceStatus] ?? ''
- })
- tableBody.push(
- [item.deptName, item.nickName, '上午', ...data1],
- [item.deptName, item.nickName, '下午', ...data2]
- )
- }
- return [...tableHead, ...tableBody]
- }
- /** 考勤导入 */
- const importFormRef = ref()
- const handleImport = () => {
- importFormRef.value.open()
- }
- /** 初始化 */
- const init = () => {
- let toMonth = moment().format('YYYY-MM')
- fromParams.value.month = toMonth
- // 加载部门树
- initTreeDeps()
- let toMoseMonths: any = []
- toMoseMonths[0] = moment().startOf('months').format('YYYY-MM-DD') + ' 00:00:00'
- toMoseMonths[1] = moment().endOf('months').format('YYYY-MM-DD') + ' 23:59:59'
- initInsMouth(toMoseMonths)
- }
- onMounted(() => {
- init()
- })
- /** 免考勤人员设置 */
- const dialogVisible = ref(false)
- const notAttendanceList = ref([])
- const noAttendanceSet = () => {
- dialogVisible.value = false
- // console.log('notAttendanceList', notAttendanceList.value)
- }
- </script>
- <style lang="scss" scoped>
- .attendanceCenterDep {
- position: relative;
- width: 100%;
- height: calc(100% - 15px);
- padding: 15px 30px;
- margin-top: 15px;
- background: #fff;
- border-radius: 10px;
- h1 {
- margin-bottom: 20px;
- font-size: 20px;
- font-weight: 600;
- color: #121518;
- }
- .depSearch {
- display: flex;
- width: 100%;
- margin-bottom: 20px;
- align-items: center;
- justify-content: space-between;
- .depSearchFlex {
- display: flex;
- align-items: center;
- .searBox {
- display: flex;
- align-items: center;
- margin-right: 25px;
- .span {
- font-size: 16px;
- white-space: nowrap;
- }
- }
- }
- .depStateBox {
- display: flex;
- width: 30px;
- height: 30px;
- cursor: pointer;
- border: 2px solid #e1e4ea;
- border-radius: 50%;
- align-items: center;
- justify-content: center;
- p {
- font-size: 16px;
- font-weight: 600;
- color: #e1e4ea;
- }
- }
- }
- .depTable {
- width: 100%;
- height: calc(100% - 140px);
- }
- .depPages {
- position: absolute;
- right: 30px;
- bottom: 10px;
- }
- }
- :deep(.depTable) {
- .el-table {
- .workDayClass {
- color: pink;
- }
- .is-group {
- height: 80px;
- tr {
- background-color: #e5f0fb;
- th {
- font-weight: 500;
- color: #233755;
- background-color: #e5f0fb;
- border-color: #fff;
- }
- }
- }
- .cell {
- width: 100%;
- height: 100%;
- padding: 0 !important;
- font-size: 16px !important;
- text-align: center;
- }
- .el-table__cell {
- // padding: 0;
- }
- .el-table__body-wrapper {
- tr {
- td {
- border: 0;
- }
- }
- .el-table__row--striped {
- td {
- background-color: #f7f7f7;
- }
- }
- }
- .iconsFlex {
- display: flex;
- width: 100%;
- height: 100%;
- align-items: center;
- flex-wrap: wrap;
- .spans {
- width: 100%;
- height: 24px;
- font-size: 14px !important;
- font-weight: 800;
- color: #f23c3c;
- cursor: pointer;
- user-select: none;
- margin: 0;
- padding: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .sw {
- display: flex;
- width: 100%;
- height: 50%;
- user-select: none;
- align-items: center;
- justify-content: center;
- }
- .sw:hover {
- .spans {
- color: #989fa8;
- }
- }
- .xw:hover {
- .spans {
- color: #989fa8;
- }
- }
- .xw {
- display: flex;
- width: 100%;
- height: 50%;
- user-select: none;
- align-items: center;
- justify-content: center;
- }
- }
- }
- .labelModeRl {
- writing-mode: vertical-rl;
- }
- }
- :deep(.workday) {
- color: #989fa8 !important;
- }
- </style>
|