1. 程式人生 > 實用技巧 >工作中製造的一個react日曆輪子

工作中製造的一個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;
  }
}

這樣就完成了 效果如下

當然,當然還有回顯效果,圖片沒有展示出來。