/* eslint-disable global-require */ import React, { useState, useEffect, useRef } from 'react'; import axios from 'axios'; import { Calendar, Button, Select, Space, message, Popconfirm, Modal, Form, Input, Radio, Upload, AutoComplete, } from 'antd'; import { ImportOutlined, DeleteOutlined, ExclamationCircleOutlined, ExportOutlined, PlusOutlined, SyncOutlined, LeftOutlined, RightOutlined, } from '@ant-design/icons'; import { AddFlowHoliday, GetWorkDayManageInfo, DeleteFlowHoliday, DownLoadFlowHoliday, ImportFlowHoliday, } from '@/services/holidays/holidays'; import { calendar } from 'js-calendar-converter'; import moment from 'moment'; import 'moment/dist/locale/zh-cn'; import locale from 'antd/es/date-picker/locale/zh_CN'; import classNames from 'classnames'; import WorkTiemConfig from './components/WorkTiemConfig'; import AddModal from './components/AddModal'; import Synchronize from './components/Synchronize'; import HolidayConfig from './components/HolidayConfig'; import styles from './holidays.less'; const { confirm } = Modal; const holidaysList = new Set(['元旦', '春节', '清明节', '劳动节', '端午节', '中秋节', '国庆节']); moment.updateLocale('zh-cn', { weekdaysMin: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], }); const Holidays = () => { const [holidayStatistics, setHolidayStatistics] = useState({ SumDays: { type: 'SumDays', name: '总天数', value: 0, icon: require('../../../../assets/images/holidays/SumDays.png'), color: '#3D78FF', }, WeekendDays: { type: 'WeekendDays', name: '周末天数', value: 0, icon: require('../../../../assets/images/holidays/WeekendDays.png'), color: '#FF5933', }, Holidays: { type: 'Holidays', name: '法定假日', value: 0, icon: require('../../../../assets/images/holidays/Holiday.png'), color: '#FFB02E', }, TakeDays: { type: 'TakeDays', name: '调休天数', value: 0, icon: require('../../../../assets/images/holidays/TakeDays.png'), color: '#8687FF', }, }); const [size, setSize] = useState({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, }); const [visible, setVisible] = useState({ holiday: false, workTime: false, synchronize: false, calendar: false, }); const workTime = useRef([ { icon: require('../../../../assets/images/holidays/icon1.png'), name: '上午', from: '', to: '', type: 1, }, { icon: require('../../../../assets/images/holidays/icon2.png'), name: '下午', from: '', to: '', type: 2, }, { icon: require('../../../../assets/images/holidays/icon3.png'), name: '晚上', from: '', to: '', type: 3, }, ]); // 工作时间 const workTimeMsg = useRef({}); const calendarHoliday = useRef(new Map()); // 日历上得假期 const queryData = useRef({ year: new Date().getFullYear(), month: new Date().getMonth() + 1 }); const tableData = useRef([]); const editDateMsg = useRef({ isRest: '', date: '', msg: '', holidayOptions: '' }); const [currentDate, setCurrentDate] = useState(moment().format('YYYY-MM-DD')); const [form] = Form.useForm(); const [formRest] = Form.useForm(); useEffect(() => { document.querySelector('.ant-picker-content').setAttribute('border', 1); let year = new Date().getFullYear(); let month = new Date().getMonth() + 1; getData(year, month); resizeListener(); window.addEventListener('resize', resizeListener); return () => { window.removeEventListener('resize', resizeListener); document.querySelectorAll('.ant-popconfirm').forEach(ele => { ele.style.zoom = 'normal'; }); document.querySelectorAll('.ant-select-dropdown').forEach(ele => { ele.style.zoom = 'normal'; }); }; }, []); const resizeListener = () => { console.log(document.documentElement.clientWidth); setSize({ width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, }); }; const getData = (year, month) => { GetWorkDayManageInfo({ year, month }).then(res => { if (res.code === 0) { calendarHoliday.current = new Map([]); res.data.CalendarHoliday.forEach(item => { calendarHoliday.current.set(item.DayDate, item); }); tableData.current = res.data.HolidayList; workTime.current.forEach(item => { switch (item.name) { case '上午': item.from = res.data.WorkTime ? res.data.WorkTime.WORKTIME_MOR_FROM : '00:00'; item.to = res.data.WorkTime ? res.data.WorkTime.WORKTIME_MOR_TO : '00:00'; break; case '下午': item.from = res.data.WorkTime ? res.data.WorkTime.WORKTIME_AFT_FROM : '00:00'; item.to = res.data.WorkTime ? res.data.WorkTime.WORKTIME_AFT_TO : '00:00'; break; case '晚上': item.from = res.data.WorkTime ? res.data.WorkTime.WORKTIME_EVE_FROM : '00:00'; item.to = res.data.WorkTime ? res.data.WorkTime.WORKTIME_EVE_TO : '00:00'; break; default: break; } }); console.log(workTime.current, 'workTime.current'); const obj = JSON.parse(JSON.stringify(holidayStatistics)); obj.Holidays.value = res.data.HolidayStatistics?.Holidays; obj.SumDays.value = res.data.HolidayStatistics?.SumDays; obj.TakeDays.value = res.data.HolidayStatistics?.TakeDays; obj.WeekendDays.value = res.data.HolidayStatistics?.WeekendDays; setHolidayStatistics(obj); // setFlag(flag + 1); } }); }; // 删除节假日 const delRow = record => { console.log(record, 'record'); let dayDate = record.DayDate; DeleteFlowHoliday({ dayDate }).then(res => { if (res.code === 0) { message.success('删除成功'); getData(queryData.current.year, queryData.current.month); } else { message.error(res.msg); } }); }; // 日历日期变化监听 const onPanelChange = (value, mode) => { console.log(value, mode, '监听'); // eslint-disable-next-line no-underscore-dangle let time = value._d; let year = time.getFullYear(); let month = time.getMonth() + 1; let day = time.getDate(); queryData.current.year = year; queryData.current.month = month; getData(year, month); }; // 点击到的日期 const onSelect = (value, obj) => { let date = value.format('YYYY-MM-DD'); let options = tableData.current.map(item => ({ value: item.DayName, label: item.DayName, })); editDateMsg.current = { isRest: calendarHoliday.current.has(date), date, msg: obj, holidayOptions: options, }; setVisible({ ...visible, calendar: true }); }; // 日历渲染替换 const dateCellRender = value => { // eslint-disable-next-line no-underscore-dangle let time = value._d; let year = time.getFullYear(); let month = time.getMonth() + 1; let day = time.getDate(); let week = time.getDay(); month = month < 10 ? `0${month}` : month; day = day < 10 ? `0${day}` : day; let obj = calendar.solar2lunar(year, month, day); let date = `${year}-${month}-${day}`; let holidayMsg = calendarHoliday.current?.get(date); let today = moment(new Date()).format('YYYY-MM-DD'); return ( <div key={date} className={classNames(styles.calendarCell, { [styles.otherMonthDay]: month != queryData.current.month, [styles.rest]: holidayMsg?.DayType === 1, [styles.holidays]: holidayMsg?.DayType === 3, [styles.weekend]: week === 0 || week === 6, [styles.choose]: date === currentDate, [styles.today]: date === today, })} onDoubleClick={() => onSelect(value, obj)} onClick={e => { setCurrentDate(date); }} > <div className={styles.tiemBox}> <p> {time.getDate()}</p> <p> {obj.lunarFestival || obj.festival || obj.Term || obj.IDayCn}</p> </div> <div className={styles.tips} style={{ display: week === 0 || week === 6 ? 'block' : 'none', background: calendarHoliday.current.has(date) ? '#FAAD14' : '#3D78FF', }} > {calendarHoliday.current.has(date) ? '休' : '班'} </div> <div className={styles.icon} style={{ display: calendarHoliday.current.has(date) ? 'block' : 'none' }} /> </div> ); }; // 日历头部渲染 const headerRender = ({ value, onChange }) => { const start = 0; const end = 12; const monthOptions = []; const current = value.clone(); const localeData = value.localeData(); const months = []; for (let i = 0; i < 12; i++) { current.month(i); months.push(localeData.monthsShort(current)); } for (let i = start; i < end; i++) { monthOptions.push( <Select.Option key={i} value={i} className="month-item"> {months[i]} </Select.Option>, ); } const year = value.year(); const month = value.month(); const options = []; for (let i = year - 10; i < year + 10; i += 1) { options.push( <Select.Option key={i} value={i} className="year-item"> {i} </Select.Option>, ); } return ( <div className={styles.calendarHeader}> <div className={styles.left}> <LeftOutlined onClick={() => { let newYear = year - 1; const now = value.clone().year(newYear); onChange(now); }} /> <Select style={{ margin: '0 5px', width: '80px' }} dropdownMatchSelectWidth={false} className="my-year-select" value={year} onDropdownVisibleChange={() => { setTimeout(() => { console.log(document.querySelectorAll('.ant-select-dropdown')); document.querySelectorAll('.ant-select-dropdown').forEach(ele => { ele.style.zoom = size.width / 1920; }); }, 0); }} onChange={newYear => { console.log(newYear); const now = value.clone().year(newYear); onChange(now); }} > {options} </Select> <RightOutlined onClick={() => { let newYear = year + 1; const now = value.clone().year(newYear); onChange(now); }} /> <LeftOutlined onClick={() => { let val = value.clone(); let newMonth = month - 1; let now; if (newMonth < 1) { let newYear = year - 1; now = val.set({ year: newYear, month: 11 }); } else { now = value.clone().month(newMonth); } console.log(now.format('YYYY-MM-DD')); onChange(now); }} /> <Select style={{ margin: '0 5px', width: '80px' }} dropdownMatchSelectWidth={false} onDropdownVisibleChange={() => { setTimeout(() => { console.log(document.querySelectorAll('.ant-select-dropdown')); document.querySelectorAll('.ant-select-dropdown').forEach(ele => { ele.style.zoom = size.width / 1920; }); }, 0); }} value={month} onChange={newMonth => { console.log(newMonth); const now = value.clone().month(newMonth); onChange(now); }} > {monthOptions} </Select> <RightOutlined onClick={() => { let val = value.clone(); let newMonth = month + 1; let now; if (newMonth > 12) { newMonth = 1; now = val.year(year + 1).month(newMonth); } else { now = value.clone().month(newMonth); } onChange(now); }} /> </div> <div className={styles.right}> <Upload showUploadList={false} accept=".json" beforeUpload={beforeUpload}> <Button style={{ marginLeft: '10px' }} type="primary" ghost> <div style={{ display: 'flex', alignItems: 'center' }}> <ImportOutlined style={{ marginRight: '5px' }} /> <div> 导入</div> </div> </Button> </Upload> <Button onClick={hadelExport} style={{ marginLeft: '10px' }} type="primary" ghost> <div style={{ display: 'flex', alignItems: 'center' }}> <ExportOutlined style={{ marginRight: '5px' }} /> <div> 导出</div> </div> </Button> <Button onClick={() => setVisible({ ...visible, holiday: true })} style={{ marginLeft: '10px' }} type="primary" > <div style={{ display: 'flex', alignItems: 'center' }}> <PlusOutlined style={{ marginRight: '5px' }} /> <div> 新增</div> </div> </Button> <Button onClick={() => setVisible({ ...visible, synchronize: true })} style={{ marginLeft: '10px', background: '#FAAD14', border: 'none', color: '#fff' }} > <div style={{ display: 'flex', alignItems: 'center' }}> <SyncOutlined style={{ marginRight: '5px' }} /> <div>一键同步</div> </div> </Button> </div> </div> ); }; // 导出 const hadelExport = () => { window.location.href = DownLoadFlowHoliday({ year: queryData.current.year, }); }; // 提交上传文件 const beforeUpload = val => { console.log(val); // if (!form.getFieldValue('fileName')) { // message.info('请上传文件'); // return; // } const formData = new FormData(); formData.append('_files', val); ImportFlowHoliday(formData) .then(res => { console.log(res); if (res.code === 0) { getData(queryData.current.year, queryData.current.month); message.success('上传成功'); } else { message.error(res.msg); } }) .catch(() => { message.error('网络异常,请稍后再试'); }); }; // 保存工作时间 const saveWorkTime = () => { setVisible({ ...visible, workTime: false }); getData(queryData.current.year, queryData.current.month); }; // 添加节假日 const addHoliday = () => { setVisible({ ...visible, holiday: false }); getData(queryData.current.year, queryData.current.month); }; // 同步节假日 const startSynchronize = () => { setVisible({ ...visible, synchronize: false }); getData(queryData.current.year, queryData.current.month); }; // 编辑工作时间 const editWorkTime = value => { workTimeMsg.current = value; setVisible({ ...visible, workTime: true }); }; const disabledDate = value => { return ( value < moment(`${queryData.current.year}-${queryData.current.month}`).startOf('month') || value > moment(`${queryData.current.year}-${queryData.current.month}`).endOf('month') ); }; // 日历保存回调 const calendarSubmit = () => { setVisible({ ...visible, calendar: false }); getData(queryData.current.year, queryData.current.month); }; return ( <div className={styles.calendarPage} // style={{ transform: `scale(${size.width / 1920}, ${size.height / 960})` }} style={{ zoom: size.width / 1920 }} > {/* 左侧日历 */} <div className={styles.leftContent}> <div className={styles.calendarBox}> <Calendar disabledDate={disabledDate} prevFarthestDate={false} locale={locale} dateFullCellRender={dateCellRender} onPanelChange={onPanelChange} headerRender={headerRender} onSelect={() => {}} /> </div> {/* 工作时间管理 */} <div className={styles.workTiemContainer}> <div className={styles.header}> <div className={styles.headerLeft}> <div className={styles.lineBox} /> <div className={styles.text}>工作时间管理</div> </div> </div> <div className={styles.content}> {workTime.current?.map(item => ( <div title="点击修改工作时间" className={classNames(styles.workTimeBox, { [styles.mor]: item.name === '上午', [styles.aft]: item.name === '下午', [styles.eve]: item.name === '晚上', })} key={item.name} onClick={() => editWorkTime(item)} > <div className={styles.left}> <div className={styles.icon}> <img src={item.icon} alt="" /> </div> </div> <div className={styles.right}> <div className={styles.title}>{item.name}</div> <div className={styles.time}> {item.from}-{item.to} </div> </div> </div> ))} </div> </div> </div> <div className={styles.rightContent}> <div className={styles.header}> <div className={styles.lineBox} /> <div className={styles.title}>节假日列表</div> </div> <div className={styles.statisticsBox}> {Object.values(holidayStatistics).map(item => ( <div className={styles.statistics} type={item.type} key={item.name}> <div className={styles.left}> <div className={styles.icon}> <img src={item.icon} alt="" /> </div> </div> <div className={styles.right}> <div className={styles.desBox}> <div className={styles.label}>{item.name}</div> <div className={styles.value}> <i>{item.value}</i> 天 </div> </div> <div className={styles.progress}> <div className={styles.rate} style={{ background: item.color, width: `${(item.value / holidayStatistics.SumDays.value) * 100}%`, }} /> </div> </div> </div> ))} </div> <div className={styles.holidaysList}> {tableData.current.map((item, index) => ( <div className={styles.itemBox} key={index}> <div className={styles.header}> <div className={styles.title}> {item.DayName}({item.DayDate?.split(',').length}天) <img src={require('/src/assets/images/holidays/Holiday.png')} alt="" style={{ width: '25px', marginLeft: '10px', display: holidaysList.has(item.DayName) ? 'inline-block' : 'none', }} /> </div> <div className={styles.del} onClick={() => { setTimeout(() => { console.log(document.querySelectorAll('.ant-popconfirm')); document.querySelectorAll('.ant-popconfirm').forEach(ele => { ele.style.zoom = size.width / 1920; }); }, 0); }} > <Popconfirm title="是否删除该节假日?" onConfirm={() => delRow(item)} onCancel={() => message.error('取消删除')} okText="是" cancelText="否" style={{ zoom: size.width / 1920 }} > <DeleteOutlined style={{ fontSize: '16px', color: '#e86060' }} /> </Popconfirm> </div> </div> <div className={styles.content}> {item.DayDate?.split(',').map((ele, i) => <span key={i}>{ele}</span>)} </div> </div> ))} </div> </div> <WorkTiemConfig visible={visible.workTime} onSubumit={saveWorkTime} handleCancel={() => setVisible({ ...visible, workTime: false })} msg={workTimeMsg.current} allTime={workTime.current} /> <AddModal visible={visible.holiday} onSubumit={addHoliday} handleCancel={() => setVisible({ ...visible, holiday: false })} /> <Synchronize visible={visible.synchronize} onSubumit={startSynchronize} handleCancel={() => setVisible({ ...visible, synchronize: false })} /> <HolidayConfig visible={visible.calendar} onSubumit={calendarSubmit} msg={editDateMsg.current} handleCancel={() => setVisible({ ...visible, calendar: false })} /> </div> ); }; export default Holidays;