import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Select, Radio, Row, Col, Popover, Badge, Spin } from 'antd'
|
import moment from 'moment'
|
|
import './index.scss'
|
|
const { Option } = Select
|
|
class Calendar extends Component {
|
static propTpyes = {
|
data: PropTypes.any, // 事件数据
|
calendar: PropTypes.any,
|
changeDate: PropTypes.func,
|
triggerDate: PropTypes.func,
|
loading: false
|
}
|
|
state = {
|
level: 'day',
|
levels: null,
|
yearlist: null,
|
selectYear: moment().format('YYYY'),
|
selectMonth: moment().format('MM'),
|
datelist: null,
|
monthlist: null
|
}
|
|
UNSAFE_componentWillMount() {
|
const { calendar, data } = this.props
|
|
let yearlist = []
|
let _selectYear = +this.state.selectYear
|
yearlist.push(`${_selectYear}`)
|
|
for (let i = 1; i <= 50; i++) {
|
yearlist.unshift(`${_selectYear - i}`)
|
yearlist.push(`${_selectYear + i}`)
|
}
|
|
let datelist = this.getDateList(this.state.selectYear)
|
|
if (data && data.length > 0) {
|
datelist = this.mountdata(datelist, data)
|
}
|
|
let _levels = calendar.levels
|
let level = _levels[0]
|
let monthlist = null
|
|
if (_levels.includes('month') || _levels.includes('year')) {
|
monthlist = datelist.filter(item => item.month === moment().format('MM'))[0]
|
}
|
|
this.setState({
|
yearlist,
|
datelist,
|
monthlist,
|
level,
|
levels: _levels
|
})
|
}
|
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
if (!is(fromJS(this.props.data), fromJS(nextProps.data))) {
|
let datelist = this.getDateList(this.state.selectYear)
|
let monthlist = null
|
|
datelist = this.mountdata(datelist, nextProps.data)
|
|
if (this.state.levels.includes('month') || this.state.levels.includes('year')) {
|
monthlist = datelist.filter(item => item.month === this.state.selectMonth)[0]
|
}
|
|
this.setState({
|
datelist: datelist,
|
monthlist
|
})
|
} else if (!is(fromJS(this.props.calendar), fromJS(nextProps.calendar))) {
|
this.setState({
|
levels: nextProps.calendar.levels
|
})
|
}
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.props.loading), fromJS(nextProps.loading)) || !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
mountdata = (datelist, data) => {
|
const { calendar } = this.props
|
|
let datalist = []
|
let levels = { red: 1, orange: 2, yellow: 3, green: 4, cyan: 5, blue: 6, purple: 7, gray: 8 }
|
let colors = { red: '#d0021b', orange: '#f5a623', yellow: '#f8e71c', green: '#7ed321', cyan: '#50e3c2', blue: '#1890ff', purple: '#bd10e0', gray: '#9b9b9b' }
|
|
data && data.forEach(item => {
|
let startTime = item[calendar.startfield]
|
let endTime = item[calendar.endfield]
|
let color = item[calendar.colorfield]
|
|
if (!startTime || !/^(1|2)\d{3}(-|\/)\d{2}(-|\/)\d{2}/.test(startTime)) return
|
if (!endTime || !/^(1|2)\d{3}(-|\/)\d{2}(-|\/)\d{2}/.test(endTime)) return
|
if (!item[calendar.remarkfield]) return
|
|
let equal = endTime.substr(0, 4) === startTime.substr(0, 4)
|
|
datalist.push({
|
color: colors[color] || '',
|
level: color && levels[color] ? levels[color] : 100,
|
remark: item[calendar.remarkfield],
|
startMonth: startTime.substr(0, 4) + startTime.substr(5, 2),
|
endMonth: endTime.substr(0, 4) + endTime.substr(5, 2),
|
start: startTime.substr(0, 4) + startTime.substr(5, 2) + startTime.substr(8, 2),
|
startTime: equal ? `${startTime.substr(5, 2)}-${startTime.substr(8, 2)}` : `${startTime.substr(0, 4)}-${startTime.substr(5, 2)}-${startTime.substr(8, 2)}`,
|
end: endTime.substr(0, 4) + endTime.substr(5, 2) + endTime.substr(8, 2),
|
endTime: equal ? `${endTime.substr(5, 2)}-${endTime.substr(8, 2)}` : `${endTime.substr(0, 4)}-${endTime.substr(5, 2)}-${endTime.substr(8, 2)}`
|
})
|
})
|
|
if (datalist.length > 0) {
|
datalist.sort((a, b) => a.level - b.level)
|
}
|
|
let styles = [
|
{background: '#d0021b', color: '#ffffff'},
|
{background: '#f5a623', color: '#ffffff'},
|
{background: '#f8e71c', color: '#ffffff'},
|
{background: '#7ed321', color: '#ffffff'},
|
{background: '#50e3c2', color: '#ffffff'},
|
{background: '#1890ff', color: '#ffffff'},
|
{background: '#bd10e0', color: '#ffffff'},
|
{background: '#9b9b9b', color: '#ffffff'},
|
]
|
|
return datelist.map(month => {
|
month.subData = []
|
datalist.forEach(item => {
|
if (item.startMonth <= month.time && item.endMonth >= month.time) {
|
month.subData.push(item)
|
}
|
})
|
if (month.subData[0]) {
|
month.style = styles[month.subData[0].level - 1] || null
|
}
|
month.sublist = month.sublist.map(week => {
|
week.sublist = week.sublist.map(day => {
|
if (!day) return null
|
|
day.subData = []
|
datalist.forEach(item => {
|
if (item.start <= day.time && item.end >= day.time) {
|
day.subData.push(item)
|
}
|
})
|
|
if (day.subData[0]) {
|
day.style = styles[day.subData[0].level - 1] || null
|
}
|
return day
|
})
|
return week
|
})
|
return month
|
})
|
}
|
|
getDateList = (selectYear) => {
|
let datelist = []
|
let months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
|
let monthName = {
|
'01': '一月', '02': '二月', '03': '三月', '04': '四月', '05': '五月', '06': '六月',
|
'07': '七月', '08': '八月', '09': '九月', '10': '十月', '11': '十一月', '12': '十二月'
|
}
|
|
months.forEach(month => {
|
let _weeklist = [{week: 1, sublist: []}]
|
let _week = moment(`${selectYear}${month}01`, 'YYYYMMDD').weekday()
|
let end = +moment(`${selectYear}${month}`, 'YYYYMM').endOf('month').format('DD')
|
|
for (let i = 0; i < _week; i++) {
|
_weeklist[0].sublist.push(null)
|
}
|
|
for (let i = 1; i <= end; i++) {
|
let _day = i < 10 ? `0${i}` : `${i}`
|
|
if (_weeklist[_weeklist.length - 1].sublist.length < 7) {
|
_weeklist[_weeklist.length - 1].sublist.push({day: _day, time: selectYear + month + _day, label: i, subData: []})
|
} else {
|
let _week = {week: _weeklist.length + 1, sublist: [{day: _day, time: selectYear + month + _day, label: i, subData: []}]}
|
_weeklist.push(_week)
|
}
|
}
|
|
let re = 7 - _weeklist[_weeklist.length - 1].sublist.length
|
for (let i = 0; i < re; i++) {
|
_weeklist[_weeklist.length - 1].sublist.push(null)
|
}
|
|
datelist.push({
|
month: month,
|
time: selectYear + month,
|
label: monthName[month],
|
sublist: _weeklist,
|
subData: []
|
})
|
})
|
|
return datelist
|
}
|
|
levelChange = (e) => {
|
this.setState({ level: e.target.value })
|
}
|
|
yearChange = (value) => {
|
const { calendar, data } = this.props
|
const { levels, selectMonth } = this.state
|
let datelist = this.getDateList(value)
|
let monthlist = null
|
|
if (levels.includes('month') || levels.includes('year')) {
|
monthlist = datelist.filter(item => item.month === selectMonth)[0]
|
}
|
|
if (calendar.refresh !== 'true') {
|
datelist = this.mountdata(datelist, data)
|
|
this.setState({ selectYear: value, datelist, monthlist })
|
} else {
|
this.setState({ selectYear: value, datelist, monthlist }, () => {
|
this.props.changeDate(value)
|
})
|
}
|
}
|
|
monthChange = (value) => {
|
const { datelist, levels, selectYear } = this.state
|
|
if (!levels.includes('month')) {
|
if (this.props.triggerDate) {
|
this.props.triggerDate({
|
time: `${selectYear}${value}01`
|
})
|
}
|
return
|
}
|
|
this.setState({
|
level: 'month',
|
selectMonth: value,
|
monthlist: datelist.filter(item => item.month === value)[0]
|
})
|
}
|
|
triggerDay = (item) => {
|
if (this.props.triggerDate) {
|
this.props.triggerDate(item)
|
}
|
}
|
|
render() {
|
const { loading } = this.props
|
const { level, selectMonth, selectYear, yearlist, levels, datelist, monthlist } = this.state
|
const _levelName = {day: '日', month: '月', year: '年'}
|
|
return (
|
<div className="mk-calendar">
|
{loading ? <div className="loading-data"><Spin /></div> : null}
|
<div className="mk-calendar-control">
|
<Select value={selectYear} onChange={this.yearChange}>
|
{yearlist.map(item => (<Option key={item} value={item}>{item}年</Option>))}
|
</Select>
|
{level === 'month' ? <Select value={selectMonth} onChange={this.monthChange}>
|
<Option value="01">1月</Option>
|
<Option value="02">2月</Option>
|
<Option value="03">3月</Option>
|
<Option value="04">4月</Option>
|
<Option value="05">5月</Option>
|
<Option value="06">6月</Option>
|
<Option value="07">7月</Option>
|
<Option value="08">8月</Option>
|
<Option value="09">9月</Option>
|
<Option value="10">10月</Option>
|
<Option value="11">11月</Option>
|
<Option value="12">12月</Option>
|
</Select> : null}
|
{levels.length > 1 ? <Radio.Group value={level} onChange={this.levelChange}>
|
{levels.map(item => (<Radio.Button key={item} value={item}>{_levelName[item]}</Radio.Button>))}
|
</Radio.Group> : null}
|
</div>
|
<div className="mk-calendar-content">
|
{level === 'day' ? <Row className="day-calendar" gutter={16}>
|
{datelist.map(item => (
|
<Col span={4} key={item.month}>
|
<table>
|
<thead>
|
<tr>
|
<th colSpan="7">{item.label}</th>
|
</tr>
|
<tr>
|
<th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th><th>日</th>
|
</tr>
|
</thead>
|
<tbody>
|
{item.sublist.map(cell => (
|
<tr key={cell.week}>
|
{cell.sublist.map((d, i) => (
|
<td key={i}>
|
{d ? <div className={'day-wrap ' + d.class} style={d.style || null} onClick={() => this.triggerDay(d)}>
|
{d.subData.length > 0 ? <Popover mouseEnterDelay={0.3} overlayClassName="calendar-day-pop" content={
|
<div>
|
{d.subData.map((data, index) => (
|
<div key={index} className="message">
|
<Badge color={data.color} text={
|
<span>
|
{data.remark}
|
<span style={{color: 'rgba(0,0,0,.45)'}}>({data.startTime + ' ~ ' + data.endTime})</span>
|
</span>}
|
/>
|
</div>
|
))}
|
</div>
|
} trigger="hover">
|
{d.label}
|
</Popover> : d.label}
|
</div> : null }
|
</td>
|
))}
|
</tr>
|
))}
|
</tbody>
|
</table>
|
</Col>
|
))}
|
</Row> : null}
|
{level === 'month' && monthlist ? <div className="month-calendar">
|
<table>
|
<thead>
|
<tr>
|
<th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th><th>日</th>
|
</tr>
|
</thead>
|
<tbody>
|
{monthlist.sublist.map(cell => (
|
<tr key={cell.week}>
|
{cell.sublist.map((d, i) => (
|
<td key={i}>
|
{d ? <div className="month-wrap" style={d.style || null} onClick={() => this.triggerDay(d)}>
|
<div className="header">
|
{d.label}
|
</div>
|
<ul className="content">
|
{d.subData.map((data, index) => (
|
<li key={index} className="message">
|
<Badge color={d.style ? (data.color === d.style.background ? '#ffffff' : data.color) : data.color} text={data.remark} />
|
</li>
|
))}
|
</ul>
|
</div> : null }
|
</td>
|
))}
|
</tr>
|
))}
|
</tbody>
|
</table>
|
</div>: null}
|
{level === 'year' && monthlist ? <Row className="year-calendar">
|
{datelist.map(item => (
|
<Col span={8} key={item.month}>
|
<div className="year-wrap" style={item.style || null} onClick={() => this.monthChange(item.month)}>
|
<div className="header" style={item.style ? null : {color: '#1890ff'}}>
|
{item.label}
|
</div>
|
<ul className="content">
|
{item.subData.map((data, index) => (
|
<li key={index} className="message">
|
<Badge color={item.style ? (data.color === item.style.background ? '#ffffff' : data.color) : data.color} text={`${data.remark} (${data.startTime} ~ ${data.endTime})`}/>
|
</li>
|
))}
|
</ul>
|
</div>
|
</Col>
|
))}
|
</Row>: null}
|
</div>
|
</div>
|
)
|
}
|
}
|
|
export default Calendar
|