工作中制造的一个react日历轮子
需求需要在日历上实现多选但不连续的日期,ant design 的日历组件只能单选或者连续选择一段日期,所以只能现造了一个
import React, { Component } from "react"; import { routerRedux } from "dva/router"; import { connect } from "dva"; import { Menu, Icon, Form, Select, Row, Col, Card, Button, message, TimePicker, InputNumber, } from "antd"; import moment from "moment"; // import './ReserveList1.css'; import styles from "./ReserveList1.less"; import PageHeaderWrapper from "@/components/PageHeaderWrapper"; const { Option } = Select; const FormItem = Form.Item; // console.log(moment); const DATE_FORMAT = "YYYY-MM-DD"; const holidayMap = { // "2019-05-01": "劳动节221", // "2019-05-02": "劳动节2", // "2019-05-03": "劳动节3", // "2019-05-04": "五四青年节", // "2019-06-07": "端午节1", // "2019-06-08": "端午节2", // "2019-06-09": "端午节3", // "2020-07-09": "已预约", // "2020-07-10": "已预约", }; // 后台返回的已预约的日期 let reverMap = [ // { subscribeDate: "2020-07-01" }, // { subscribeDate: "2020-07-02" }, // { subscribeDate: "2020-07-03" }, // { subscribeDate: "2020-07-04" }, ]; // 点击已选择的日期 let reverParam = []; class queryForm extends React.Component { constructor(props) { super(props); this.state = { options: { years: [], months: [], }, }; } state = {}; componentDidMount = function() { let mySelf = this; reverParam = []; mySelf.init(); }; init = () => { let mySelf = this; mySelf.initOptions(); mySelf.setDefaultOption(); }; initOptions = function() { let mySelf = this; let options = mySelf.state.options; let now = moment(); const yearOfNow = now.year(); let yearsOptions = []; let monthsOptions = []; // const startYear = yearOfNow - 20; const startYear = yearOfNow; const endYear = yearOfNow + 10; const startMonth = 1; const endMonth = 12; for (let i = startYear; i < endYear; i++) { let item = { key: i, label: i, }; yearsOptions.push(item); } for (let i = startMonth; i <= endMonth; i++) { let item = { key: i, label: i, }; monthsOptions.push(item); } // console.log(platformOptions); options.years = yearsOptions; options.months = monthsOptions; mySelf.setState({ options: options, }); }; setDefaultOption = () => { let mySelf = this; let form = mySelf.props.form; // let options = mySelf.state.options; let now = moment(); const yearOfNow = now.year(); const monthOfNow = now.month() + 1; // console.log(options); let defaultYearOption = { key: yearOfNow, label: yearOfNow }; let defaultMonthOption = { key: monthOfNow, label: monthOfNow }; // console.log(defaultOption) let formData = { year: defaultYearOption, month: defaultMonthOption, }; form.setFieldsValue(formData); }; changeMonth = (pars, nowMonth) => { let mySelf = this; let form = mySelf.props.form; let res = form.getFieldsValue(); let month = Number(res.month.key) + Number(pars); if (nowMonth == "当月") { let now = moment(); const yearOfNow = now.year(); const monthOfNow = now.month() + 1; month = monthOfNow; let monthOption = { key: month, label: month }; let yearOption = { key: yearOfNow, label: yearOfNow }; // console.log(monthOption); let formData = { month: monthOption, year: yearOption, }; form.setFieldsValue(formData); mySelf.props.changeCalendar(); this.setState({ disableNext: false, disablePre: false, }); return } if (month <= 0 && pars < 0) { this.setState({ disablePre: true, }); return; } else { this.setState({ disablePre: false, }); } if (month >= 13 && pars > 0) { this.setState({ disableNext: true, }); return; } else { this.setState({ disableNext: false, }); } let monthOption = { key: month, label: month }; // console.log(monthOption); let formData = { month: monthOption, }; form.setFieldsValue(formData); mySelf.props.changeCalendar(); }; render() { let mySelf = this; // eslint-disable-next-line let { getFieldDecorator, getFieldValue } = mySelf.props.form; let { options, disablePre, disableNext } = mySelf.state; // console.log(options); return ( <div> <Row className="margin-bottom-5"> <Col span={24}> <Form layout="inline" // wrappedComponentRef={(inst) => this.queryForm = inst} > <FormItem label="年"> {getFieldDecorator("year", {})( <Select labelInValue size={"default"} placeholder="year" style={{ width: 150 }} onChange={() => setTimeout(() => { mySelf.props.changeCalendar(); }, 0) } > {options.years.map(function(item, index) { return <Option key={item.key}>{item.label}</Option>; })} </Select> )} </FormItem> <FormItem label="月"> {getFieldDecorator("month", {})( <Select labelInValue size={"default"} placeholder="month" style={{ width: 150 }} onChange={() => setTimeout(() => { mySelf.props.changeCalendar(); }, 0) } > {options.months.map(function(item, index) { return <Option key={item.key}>{item.label}</Option>; })} </Select> )} </FormItem> <FormItem> <Button type="primary" size="small" onClick={() => mySelf.changeMonth(-1)} disabled={disablePre} > 上个月 </Button> <Button type="primary" size="small" onClick={() => mySelf.changeMonth(1)} className={styles["margin-left-5"]} disabled={disableNext} > 下个月 </Button> <Button type="primary" size="small" onClick={() => mySelf.changeMonth(0,"当月")} className={styles["margin-left-5"]} > 今天 </Button> </FormItem> </Form> </Col> </Row> </div> ); } } let QueryFormComponent = Form.create()(queryForm); class queryForm1 extends React.Component { constructor(props) { super(props); this.state = { subscribeTimeObj: { subscribeTimeList: [0], }, disablePre: false, disableNext: false, }; } state = {}; componentDidMount = function() { let mySelf = this; }; addSubscribeTimeList = () => { let s = this.state.subscribeTimeObj.subscribeTimeList; s[s.length] = s.length; this.setState({ subscribeTimeObj: Object.assign( {}, { subscribeTimeList: s, } ), }); }; delSubscribeTimeList = (idx) => { let s = this.state.subscribeTimeObj.subscribeTimeList; if (s.length > 1) { s.splice(idx, 1); this.setState({ subscribeTimeObj: Object.assign( {}, { subscribeTimeList: s, } ), }); } }; render() { let mySelf = this; // eslint-disable-next-line let { getFieldDecorator, getFieldValue } = mySelf.props.form; let { subscribeTimeObj } = mySelf.state; // console.log(options); return ( <div> <Button className={styles.timeAddButton} type="primary" onClick={() => mySelf.addSubscribeTimeList()} > 添加 </Button> <div className={styles.timeSlotWrap}> {subscribeTimeObj.subscribeTimeList.map((_, idx) => ( <Row key={idx} gutter={{ md: 8 }}> <Col md={8} sm={8}> <FormItem key={"subscribeStartTime" + idx} label="" labelCol={{ span: 6 }} wrapperCol={{ span: 24 }} > {getFieldDecorator("data[" + idx + "].subscribeStartTime", { rules: [{ required: true, message: "请选择开始时间!" }], })(<TimePicker style={{ width: "100%" }} format={"HH:mm"} />)} </FormItem> </Col> <Col md={8} sm={8}> <FormItem key={"subscribeEndTime" + idx} label="" labelCol={{ span: 6 }} wrapperCol={{ span: 24 }} > {getFieldDecorator("data[" + idx + "].subscribeEndTime", { rules: [{ required: true, message: "请选择结束时间!" }], })(<TimePicker style={{ width: "100%" }} format={"HH:mm"} />)} </FormItem> </Col> <Col md={6} sm={6}> <FormItem key={"subscribeCount" + idx} labelCol={{ span: 6 }} wrapperCol={{ span: 24 }} label="" > {getFieldDecorator("data[" + idx + "].subscribeCount", { rules: [{ required: true, message: "请输入数量!" }], })( <InputNumber style={{ width: "100%" }} min={1} step={1} placeholder="可预约数量" /> )} </FormItem> </Col> <Col style={{ padding: "9px" }} md={2} sm={2}> <Icon onClick={() => mySelf.delSubscribeTimeList(idx)} type="close" /> </Col> </Row> ))} </div> </div> ); } } let QueryFormComponent1 = Form.create()(queryForm1); @connect(({ reservelist1, loading, global }) => ({ global, reservelist1, loading: loading.models.reservelist1, })) class LeCalendar extends Component { constructor(props) { super(props); this.state = { //渲染日历过程中,当前显示月的moment对象 monthDisplay: "", dates: [[], []], }; } componentDidMount = () => { let mySelf = this; mySelf.init(); mySelf.generateDates(); }; init = () => { let mySelf = this; mySelf.changeCalendar(); let todayDate = moment().format("YYYY-MM-DD"); mySelf.toSetScribeDefaultValue({ pageIndex: 1, pageSize: 100, startTime: todayDate, endTime: "2029-12-31", }); }; toSetScribeDefaultValue = async (params, cb) => { const { dispatch, account } = this.props; await dispatch({ type: "reservelist1/fetch1", payload: params, callback: (s, m, data) => { const { data: { list }, } = s; let array = []; list.forEach((e) => { array.push({ subscribeDate: e.subscribeDate }); }); // reverMap = Array.from(new Set(array)); reverMap = array; let mySelf = this; mySelf.generateDates(); cb && cb(s, m, data); }, }); }; generateDates = (reserveDate) => { let mySelf = this; let dates = []; //单个日历,显示6周 const weeksNum = 6; //每周显示7天 const weekDaysNum = 7; //日历中的今天 const todayDate = moment().format(DATE_FORMAT); //渲染日历过程中,当前显示月的moment对象 let now = mySelf.state.monthDisplay; //当前显示的月份 let nowMonth = now.month(); // console.log(nowMonth); //当月第一天 let firstDayOfMonth = now.clone().startOf("month"); //当月第一天是星期几 let weekOfFirstDay = firstDayOfMonth.isoWeekday(); //日历上的当月显示的第一天 let firstDayOfMonthDisplay = firstDayOfMonth .clone() .subtract(weekOfFirstDay - 1, "days"); //渲染日历过程中,使用的当前moment对象 let currentDate = firstDayOfMonthDisplay.clone(); // console.log(firstDayOfMonthDisplay.format(DATE_FORMAT)); const otherMonthClass = "other-month"; const dayOffClass = "day-off"; const holidayClass = "holiday"; const todayClass = "today"; const lastClass = "lastDay"; const reserveClass = "reservation"; const selectClass = "selected"; for (let i = 0; i < weeksNum; i++) { let weekList = []; for (let j = 0; j < weekDaysNum; j++) { let date = currentDate.format(DATE_FORMAT); let dateDisplay = currentDate.date(); let currentMonth = currentDate.month(); let currentWeekDay = currentDate.isoWeekday(); let classNameList = []; let classNameStr = ""; let content = ""; let selectText = ""; //今天 if (todayDate === date) { classNameList.push(todayClass); } // //今天以前的日期 // if (todayDate > date) { // classNameList.push(lastClass); // } //已预约的日期 // if (reserveDate === date) { // classNameList.push(reserveClass); // } //非当前月 if (currentMonth !== nowMonth) { classNameList.push(otherMonthClass); } // //周末 // if ([6, 7].includes(currentWeekDay)) { // classNameList.push(dayOffClass); // } // 已预约的日期 let obj = {}; for (let i = 0; i < reverMap.length; i++) { const element = reverMap[i]; let value = element.subscribeDate; obj[value] = "已预约"; } if (obj[date]) { classNameList.push(holidayClass); content = obj[date]; } // 已选择的日期 let objS = {}; for (let i = 0; i < reverParam.length; i++) { const e = reverParam[i]; let value = e; objS[value] = "已预约"; } if (objS[date]) { classNameList.push(selectClass); selectText = "已选择"; } //节假日 // if (holidayMap[date]) { // classNameList.push(holidayClass); // content = holidayMap[date]; // } // //已选中的日期 // if (reserveDate === date) { // classNameList.push(selectClass); // } classNameStr = classNameList.join(" "); let item = { momentObj: currentDate, date: date, dateDisplay: dateDisplay, classNameStr: classNameStr, content: content, selectText: selectText, }; weekList.push(item); currentDate.add(1, "days"); } dates.push(weekList); } mySelf.setState({ dates, }); // console.log(dates); }; changeCalendar = () => { let mySelf = this; let form = mySelf.queryForm.props.form; let res = form.getFieldsValue(); let year = Number(res.year.key); let month = Number(res.month.key) - 1; let now = moment(); now.year(year).month(month); mySelf.state.monthDisplay = now; mySelf.generateDates(); // console.log(firstDayOfMonthDisplay.format(DATE_FORMAT)); console.log(now.format(DATE_FORMAT)); }; tapDate = (value) => { const todayDate = moment().format(DATE_FORMAT); if (todayDate > value) { message.info("只能设置今天及以后的日期"); return; } // 已预约的不能再次选择 let reserveList = []; reverMap.forEach((element) => { reserveList.push(element.subscribeDate); }); if (reserveList.includes(value)) { message.info("已预约的不能再次选择"); return; } // 如果日期已选中,取消选中 if (reverParam.includes(value)) { reverParam.remove(value); this.generateDates(value); } else { reverParam.push(value); this.generateDates(value); } }; confirmAdd = () => { const { dispatch } = this.props; let mySelf = this; let form = mySelf.queryForm1.props.form; let res = form.getFieldsValue(); form.validateFields((err, fieldsValue) => { if (err) return; let params = { ...fieldsValue, data: fieldsValue.data.map((r) => { let returnData = { subscribeTimeSlot: r.subscribeStartTime.format("HH:mm") + "-" + r.subscribeEndTime.format("HH:mm"), subscribeCount: r.subscribeCount, }; // !subscribeType && (returnData.subscribeId = getUuid(32)); return returnData; }), }; if (reverParam.length == 0) { message.info("至少选择一天日期!"); return; } let reqparams = { subscribeDays: reverParam, ...params, }; dispatch({ type: "reservelist1/batchSubscribeDefaultValue", payload: reqparams, callback: (s, m, data) => { if (m == "success") { message.info("批量初始化成功"); this.props.history.push("/reservemanagement/reservelist"); form.resetFields(); reverParam = []; // this.props.navigation.dispatch(NavigationActions.navigate({ routeName: 'SearchOrder' })) } cb && cb(s, m, data); }, }); }); }; render() { // const { // reserveList1: { data = {} }, // } = this.props; let mySelf = this; let { dates } = mySelf.state; let renderWeekDays = (days) => { let dom = null; // console.log(days); dom = ( <React.Fragment> {days.map((item, index) => { if (item.classNameStr == "today holiday") { item.classNameStr = "todayHoliday"; } if (item.classNameStr == "other-month holiday") { item.classNameStr = "otherHoliday"; } let styleName = ""; // console.log(styles[item.classNameStr]); let tdDom = ( <td className={styles["unit"]} key={index} onClick={() => this.tapDate(item.date)} > {/* <div className={ item.classNameStr + 'day-cell'}> */} <div className={ styles[item.classNameStr] ? styles[item.classNameStr] : styles[ item.classNameStr.substring( item.classNameStr.length - 8 ) ] } > <div className="header"> {/* {item.date} */} {item.dateDisplay} </div> <div>{item.content}</div> <div>{item.selectText}</div> </div> </td> ); return tdDom; })} </React.Fragment> ); return dom; }; let renderWeeks = (dates) => { let dom = null; // console.log(dates); dom = ( <React.Fragment> {dates.map((item, index) => { let trDom = <tr key={index}>{renderWeekDays(item)}</tr>; return trDom; })} </React.Fragment> ); return dom; }; return ( <PageHeaderWrapper title="批量预约设置"> {/* <Card bordered={false}> <div className={styles.tableList}> */} {/* <div className={styles.tableListForm}>{this.renderForm()}</div> <div className={styles.tableListOperator}> <Button icon="plus" type="primary" onClick={() => this.handleModalVisible(true)} > 新建 </Button> <Button icon="plus" type="primary" onClick={() => this.toNewPage(true)} > 批量初始化 </Button> </div> */} {/* <StandardTable loading={loading} data={data} columns={this.columns} onChange={this.handleStandardTableChange} /> */} {/* </div> </Card> */} <div> <div className={styles.confirmClass}> <Button type="primary" size={"large"} onClick={() => this.confirmAdd()} > 确定添加 </Button> </div> <div className={styles.mainSection}> <div> <QueryFormComponent wrappedComponentRef={(inst) => (mySelf.queryForm = inst)} // confirmAdd={() => mySelf.confirmAdd()} changeCalendar={() => mySelf.changeCalendar()} /> {/* <div className="asda">asdasdasdas</div> */} <table border="1" className={styles["le-calendar-container"]}> <thead> <tr> <th>一</th> <th>二</th> <th>三</th> <th>四</th> <th>五</th> <th>六</th> <th>日</th> </tr> </thead> <tbody>{renderWeeks(dates)}</tbody> {/* <tfoot> <tr> <td>foot1</td> <td>foot2</td> </tr> </tfoot> */} </table> </div> <QueryFormComponent1 wrappedComponentRef={(inst) => (mySelf.queryForm1 = inst)} // changeCalendar={() => mySelf.changeCalendar()} confirmAdd={() => mySelf.confirmAdd()} /> {/* <QueryFormComponent1 /> */} </div> </div> </PageHeaderWrapper> ); } } export default LeCalendar;
接口部分
import { store } from '@/utils/store'; import { getSubscribeTimeSlotList, vehiclePropertiesToSelect, subscribeSetting, subscribeDefaultValue,batchSubscribeDefaultValue,getOrderList } from '@/services/api'; let currUser = store.getParam('currUser') || {}; export default { namespace: 'reservelist1', state: { data: { list: [], pagination: {} }, downloadUrl: null, vehiclePropertytypeSelect: [] }, effects: { *fetch1({ payload, callback }, { call, put }) { const response = yield call(getOrderList, { ...payload, phone: payload.phone || '', startTime: payload.startTime || null, endTime: payload.endTime || null, orderId: payload.orderId || null, plateNumber: payload.plateNumber || null, orderState: payload.orderState || null, payState: payload.payState || null, isSubscribe: payload.isSubscribe || null, vdsResult: payload.vdsResult || null, parkId: currUser.ParkId || null }) || {}; // if (callback && typeof callback === 'function') { // } let pageSize = payload.pageSize || 10; let current = payload.pageIndex || 1; console.log('response') console.log(response) callback(response) yield put({ type: 'save1', payload: { list: response.data ? (response.data.list || []) : [], pagination: { total: response.data ? (response.data.totalCount || 0) : 0, pageSize, current }, // downloadUrl: response.data && response.data.totalCount ? getDownloadUrl('orderExport', { // ...payload, // parkId: parkId // }) : null } }) }, *sourceToSelect(_, { call, put }) { const vehiclePropertytype = yield call(vehiclePropertiesToSelect); yield put({ type: 'select', payload: { vehiclePropertytypeList: vehiclePropertytype && Array.isArray(vehiclePropertytype.data) ? vehiclePropertytype.data : [] } }) }, *batchSubscribeDefaultValue({ payload, callback }, { call }) { const res = yield call(batchSubscribeDefaultValue, { ...payload, parkId: currUser.ParkId || null, operatorId: currUser.OperatorID || null }); callback && callback(res && res.code=='200', res.message); }, // *subscribeSetting({ payload, callback }, { call }) { // const res = yield call(subscribeSetting, { // ...payload, // parkId: currUser.ParkId || null, // operatorId: currUser.OperatorID || null // }); // callback && callback(res && res.code=='200', res.message); // }, *subscribeDefaultValue({ payload, callback }, { call }) { // let reqUrl = payload.operation ? subscribeSetting : subscribeDefaultValue; const res = yield call(subscribeDefaultValue, { ...payload, parkId: currUser.ParkId || null, operatorId: currUser.OperatorID || null }); callback && callback(res && res.code=='200', res.message,res.data); } }, reducers: { save1(state, { payload }) { return { ...state, data: { list: ( payload.list || [] ).map((bean, i) => ({ ...bean, key: i })).slice(), pagination: payload.pagination }, // downloadUrl: payload.downloadUrl || null } }, select(state, { payload }) { return { ...state, vehiclePropertytypeSelect: [ { VehiclePropertyName: '', VehiclePropertyNameLabel: '请选择', VehiclePropertyID: '0' }, ...payload.vehiclePropertytypeList ].map(bean => ({ ...bean, VehiclePropertyNameLabel: bean.VehiclePropertyNameLabel || bean.VehiclePropertyName })) } } }, }
样式部分
@import '~antd/lib/style/themes/default.less'; @import '~@/utils/utils.less'; .tableList { .tableListOperator { margin-bottom: 16px; button { margin-right: 8px; } } } .siteCard { width: 300px !important; border: 1px solid #f0f0f0; border-radius: 2px; } .tableListForm { :global { .ant-form-item { display: flex; margin-right: 0; margin-bottom: 24px; > .ant-form-item-label { width: auto; padding-right: 8px; line-height: 32px; } .ant-form-item-control { line-height: 32px; } } .ant-form-item-control-wrapper { flex: 1; } } .submitButtons { display: block; margin-bottom: 24px; white-space: nowrap; } } .timeSlotWrap { padding: 0 16%; } .timeAddButton { margin: auto; width: 200px; display: block; } @media screen and (max-width: @screen-lg) { .tableListForm :global(.ant-form-item) { margin-right: 24px; } } @media screen and (max-width: @screen-md) { .tableListForm :global(.ant-form-item) { margin-right: 8px; } }
最终实现效果
君不见,高堂明镜悲白发,朝如青丝暮成雪