123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- <template>
- <div class="OaCalendar">
- <el-calendar ref="calendar" v-model="todayValue">
- <template #header="{ date }">
- <div class="selectBox">
- <el-icon class="icon1" @click="selectDate('prev-month')"><ArrowLeftBold /></el-icon>
- <p>{{ date }}</p>
- <el-icon class="icon1" @click="selectDate('next-month')"><ArrowRightBold /></el-icon>
- </div>
- </template>
- <template #date-cell="{ data }">
- <!-- 仅工作日显示角标 -->
- <div
- :class="`day-cell ${getDayClass(data.day)}`"
- v-if="data.type === 'current-month'"
- @mouseover="onMouseOver(data)"
- @mouseout="onMouseOut(data)"
- >
- <p :class="data.isSelected ? 'is-selected' : ''">
- <span v-if="getDayClass(data.day) == 'square-leave'" class="glary">{{
- dayReportMap[data.day]?.show
- }}</span>
- <span v-else>{{ dayReportMap[data.day]?.show }}</span>
- </p>
- </div>
- <div v-else>
- <div class="hide-day"> </div>
- </div>
- <div class="icon" v-if="data.type === 'current-month'">
- <el-icon v-if="getDayClass(data.day) == 'square-filled'">
- <Select />
- </el-icon>
- <el-icon v-if="getDayClass(data.day) == 'square-unfilled'"><CloseBold /></el-icon>
- <el-icon v-if="getDayClass(data.day) == 'square-today'"><EditPen /></el-icon>
- <el-icon v-if="getDayClass(data.day) == 'square-future'"><MoreFilled /></el-icon>
- <span v-if="getDayClass(data.day) == 'square-leave'">假</span>
- </div>
- </template>
- </el-calendar>
- </div>
- </template>
- <script setup lang="ts">
- import request from '@/config/axios'
- import moment from 'moment'
- import { getUserInfo } from '@/utils/tool'
- import PubsubService from '@/utils/PubsubService'
- const message = useMessage()
- // 获取用户信息
- const userInfo = getUserInfo()
- const $emit = defineEmits<{
- (e: 'click', v: string | object): void
- }>()
- const calendar = ref<any>(null)
- const todayValue = ref(new Date())
- watch(todayValue, () => {
- // 1、如果请假,则不进行跳转
- const today = moment(todayValue.value).format('YYYY-MM-DD')
- const { isworkday = 0, state = '' } = dayReportMap.value[today] ?? {}
- // 2、如果选中的是今天之后的日期,则不进行点击及跳转
- const isAfterToday = moment(todayValue.value).isAfter(moment())
- if (isAfterToday) {
- todayValue.value = new Date()
- message.info('仅可对今日及之前的日志进行填报!')
- } else if (isworkday && leaveList.includes(state)) {
- todayValue.value = new Date()
- message.info('请假当天日志不需要填报!')
- } else {
- $emit('click', dayReportMap.value[moment(todayValue.value).format('YYYY-MM-DD')])
- }
- })
- // 切换月份
- const selectDate = (val) => {
- if (!calendar.value) return
- calendar.value.selectDate(val)
- if (val === 'prev-month') {
- nowMonths.value = moment(nowMonths.value).subtract(1, 'months').format('YYYY-MM')
- } else if (val === 'next-month') {
- nowMonths.value = moment(nowMonths.value).add(1, 'months').format('YYYY-MM')
- }
- const timer = setTimeout(() => {
- initDailyReportData()
- clearTimeout(timer)
- }, 128)
- }
- onMounted(() => {
- initDailyReportData().then(() => {
- // 为了方便获取提交的内容,需要重置一下日期这些内容
- $emit('click', dayReportMap.value[moment(todayValue.value).format('YYYY-MM-DD')])
- })
- // 订阅提交(暂存)事件,执行重置事件
- PubsubService.subscribe('sendReportHandle-init', () => {
- initDailyReportData().then(() => {
- // 提交后触发点击事件刷新右边的显示内容
- $emit('click', dayReportMap.value[moment(todayValue.value).format('YYYY-MM-DD')])
- })
- })
- })
- interface ItemState {
- id?: string // ID
- date: string // 日期
- year: string // 年份
- month: string // 月份
- week: string // 第几周
- dayOfWeek: string // 第几天
- isworkday: number //是否工作日
- state: string //leaveMap
- }
- // const weekNum = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']
- // 考勤状态对应字典值 2.26
- const leaveMap = {
- 1: '出勤',
- 2: '迟到',
- 3: '早退',
- 4: '旷工',
- 5: '出差',
- 11: '事假',
- 12: '病假',
- 13: '婚假',
- 14: '产假',
- 15: '陪产假',
- 16: '工伤',
- 17: '年假',
- 18: '调休',
- 19: '丧假',
- 20: '其他'
- }
- // 所有请假的代码,参考leaveMap
- const leaveList = ['5', '11', '12', '13', '14', '15', '16', '17', '18', '19']
- const dailyReportAttendances = ref<ItemState[]>([])
- const nowMonths: any = ref(moment().format('YYYY-MM'))
- // const dictMap: any = getDictOptions(DICT_TYPE.ADM_ATTENDANCE_STATUS)
- const getWorkDayList = async (): Promise<any> => {
- const nowTimeObj = moment(nowMonths.value)
- const urlApi = `/adm/workday/list`
- const params = {
- dateDay: [
- nowTimeObj.startOf('months').format('YYYY-MM-DD HH:mm:ss'),
- nowTimeObj.endOf('months').format('YYYY-MM-DD HH:mm:ss')
- ]
- }
- return await request.get({ url: urlApi, params })
- }
- const getAttendanceSheetListMine = async (): Promise<any> => {
- const nowTimeObj = moment(nowMonths.value)
- const urlApi = `/adm/attendance-sheet/list-me`
- const params = {
- attendanceTime: [
- nowTimeObj.startOf('months').format('YYYY-MM-DD HH:mm:ss'),
- nowTimeObj.endOf('months').format('YYYY-MM-DD HH:mm:ss')
- ]
- }
- return await request.get({ url: urlApi, params })
- }
- // 日报统计
- const getLogList = async (): Promise<any> => {
- const nowTimeObj = moment(nowMonths.value)
- const urlApi = `/adm/report/list`
- const params = {
- reportType: 'daily',
- reportYear: nowTimeObj.format('YYYY'),
- reportMonth: Number(nowTimeObj.format('MM')),
- userId: userInfo.id ?? ''
- }
- return await request.get({ url: urlApi, params })
- }
- const dayReportMap: any = ref({})
- const initDailyReportData = async (): Promise<any> => {
- // 获取已填写的日志
- const logList = await getLogList()
- const logObj = {}
- logList?.forEach((item) => {
- logObj[moment(item.reportStartDate).format('YYYY-MM-DD')] = item
- })
- //考勤统计
- const attendances: [] = await getAttendanceSheetListMine()
- const attendanceState: Map<string, string> = new Map()
- attendances.forEach((item) => {
- if (leaveMap[item['attendanceStatus']]) {
- attendanceState[moment(item['attendanceTime']).format('YYYY-MM-DD')] =
- item['attendanceStatus']
- }
- })
- //节假日统计
- const workdays: any[] = await getWorkDayList()
- const dailyReportList: any[] = []
- workdays.forEach((item) => {
- const date = moment(item['dateDay']).format('YYYY-MM-DD')
- const _dailyReportMap = dailyReportList[date]
- const _attendanceState = attendanceState[date]
- const isLog = logObj[date] ?? false
- if (_dailyReportMap) {
- dailyReportList.push({
- id: _dailyReportMap['id'],
- date: moment(item['dateDay']).format('YYYY-MM-DD'),
- year: item['year'],
- month: item['month'],
- week: item['week'],
- dayOfWeek: item['dayOfWeek'],
- isworkday: item['isworkday'],
- state: _dailyReportMap['state'],
- isLog,
- holidayRemark: item['holidayRemark']
- })
- } else if (_attendanceState) {
- dailyReportList.push({
- id: '',
- date: moment(item['dateDay']).format('YYYY-MM-DD'),
- year: item['year'],
- month: item['month'],
- week: item['week'],
- dayOfWeek: item['dayOfWeek'],
- isworkday: item['isworkday'],
- state: _attendanceState,
- isLog,
- holidayRemark: item['holidayRemark']
- })
- } else {
- dailyReportList.push({
- id: '',
- date: moment(item['dateDay']).format('YYYY-MM-DD'),
- year: item['year'],
- month: item['month'],
- week: item['week'],
- dayOfWeek: item['dayOfWeek'],
- isworkday: item['isworkday'],
- state: item['isworkday'] === 0 ? '' : '-1',
- isLog,
- holidayRemark: item['holidayRemark']
- })
- }
- })
- dailyReportAttendances.value = dailyReportList
- dailyReportList.forEach((item) => {
- dayReportMap.value[item.date] = { ...item, show: item.date.split('-')[2] }
- })
- }
- /**
- * 日报样式设置
- * 1、已填,绿色三角+对勾 square-filled
- * isworkday == 1, isLog
- * 2、未填,红色三角+错号 square-unfilled
- * isworkday == 0, isLog == false
- * 3、当天,蓝色三角+画笔 square-today
- * 4、未到,灰色三角+省略 square-future
- * 5、请假,橙色三角+假 square-leave
- * 6、暂存,同 未填
- */
- const getDayClass = (data) => {
- const { isworkday = 0, state = '', date = '', isLog } = dayReportMap.value[data] ?? {}
- // 是否暂存
- const isTemp = isLog?.isTemp ?? false
- const isFuture = moment(date).isAfter(moment())
- if (isTemp) {
- return 'square-unfilled'
- }
- if (date == moment().format('YYYY-MM-DD')) {
- return `square-today`
- }
- if (isworkday && isLog && !isFuture) {
- return `square-filled`
- } else if (isworkday && leaveList.includes(state) && !isFuture) {
- return `square-leave`
- } else if (isworkday && !isLog && !isFuture) {
- return `square-unfilled`
- } else if (isworkday && isFuture) {
- return `square-future`
- }
- return ''
- }
- // 鼠标移入移出事件
- const onMouseOver = (data) => {
- const isLog = dayReportMap.value[data.day]?.isLog ? true : false
- if (isLog) {
- dayReportMap.value[data.day].show = '已填'
- } else {
- dayReportMap.value[data.day].show = dayReportMap.value[data.day]?.holidayRemark ?? '未填'
- }
- }
- const onMouseOut = (data) => {
- dayReportMap.value[data.day].show = data.day.split('-')[2]
- }
- </script>
- <style lang="scss" scoped>
- .OaCalendar {
- width: 100%;
- :deep(.el-calendar) {
- .el-calendar__header {
- padding: 0 !important;
- border-bottom: none !important;
- }
- .el-calendar-table {
- border: 1px solid #e1e3ea !important;
- thead {
- background: #f8faff !important;
- }
- }
- .el-calendar-day {
- padding: 0;
- position: relative;
- .icon {
- width: 20px;
- height: 20px;
- text-align: center;
- position: absolute;
- top: 2px;
- right: -1px;
- color: #fff;
- font-size: 12px;
- font-weight: bold;
- z-index: 999;
- }
- }
- .day-cell {
- width: 100%;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- font-size: 16px;
- font-weight: 600;
- font-family: DINCond-Bold, DINCond-Bold;
- ::before {
- content: '';
- width: 0;
- height: 0;
- border-left: 30px solid transparent;
- border-bottom: 30px solid transparent;
- position: absolute;
- top: 0;
- right: 0;
- }
- .glary {
- color: #ccc;
- }
- }
- .square-filled {
- ::before {
- border-right: 30px solid #05ce9e;
- }
- }
- .square-unfilled {
- ::before {
- border-right: 30px solid #fc4308;
- }
- }
- .square-today {
- ::before {
- border-right: 30px solid #1b80eb;
- }
- }
- .square-future {
- ::before {
- border-right: 30px solid #c9cdd8;
- }
- }
- .square-leave {
- ::before {
- border-right: 30px solid #f1a256;
- }
- }
- .is-selected {
- color: #1989fa;
- }
- }
- .selectBox {
- display: flex;
- align-items: center;
- margin-bottom: 10px;
- .icon1 {
- cursor: pointer;
- }
- p {
- font-weight: 600;
- color: #000000;
- margin: 0 10px;
- user-select: none;
- cursor: pointer;
- }
- }
- .contentBox {
- width: 100%;
- height: 80px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- border: 1px solid #dee0e3;
- .ulBox {
- width: calc(100% - 120px);
- height: 100%;
- ul {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- li {
- height: 100%;
- > div {
- &:first-child {
- width: 100%;
- height: 50%;
- border: 1px solid #dee0e3;
- display: flex;
- align-items: center;
- justify-content: center;
- border-right: 0;
- border-top: 0;
- background-color: #f7f8fa;
- &.isToday {
- background-color: #2e77e6;
- > p {
- color: #fff;
- }
- }
- }
- }
- .topBox {
- p {
- color: #121518;
- user-select: none;
- }
- }
- .topBoxPa {
- p {
- color: #b9c3c9;
- }
- }
- .topBox:hover {
- p {
- color: #1b80eb;
- }
- }
- .bomBox {
- width: 100%;
- height: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- &.imgHover_-1,
- &.imgHover_99 {
- cursor: pointer;
- }
- &.imgHover_-1:hover {
- position: relative;
- &::after {
- position: absolute;
- display: block;
- content: '补';
- width: 100%;
- height: 100%;
- background-color: #fff;
- top: 0px;
- left: 0px;
- line-height: 40px;
- text-align: center;
- color: #999;
- }
- }
- &.imgHover {
- &:hover {
- border: 1px solid #f00;
- }
- }
- img {
- user-select: none;
- }
- > span {
- cursor: pointer;
- &.leave {
- color: #f00;
- cursor: default;
- }
- }
- }
- }
- li:first-child {
- .topBox {
- border-left: 0;
- }
- }
- li:last-child {
- .topBox {
- border-right: 0;
- }
- }
- }
- .infoBox {
- width: 120px;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- flex-wrap: wrap;
- background-color: #edf0f4;
- border-left: 1px solid #dee0e3;
- span {
- display: block;
- width: 100%;
- text-align: center;
- font-size: 14px;
- }
- p {
- width: 100%;
- text-align: center;
- font-size: 18px;
- }
- }
- }
- }
- </style>
|