【无标题】

文章介绍了如何在React项目中使用AntDesign的DatePicker组件,扩展其功能,包括自定义时间范围选择、显示工具选项和时间类型切换。还展示了如何在Header组件中集成时间选择和Echarts图表组件的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import type { DatePickerProps } from 'antd';
import { DatePicker, Radio } from 'antd';
import moment from 'moment';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';

export enum TimeKey {
  'hour' = 'hour',
  'today' = 'today',
  'yesterday' = 'yesterday',
  'week' = 'week',
  'month' = 'month',
  'year' = 'year',
}

export type TimeType = keyof typeof TimeKey | undefined;

type ValueType = { start: number; end: number; type: TimeType };

type timeToolOptions = {
  label: string;
  value: string;
};

interface ExtraTimePickerProps extends Omit<DatePickerProps, 'onChange' | 'value'> {
  onChange?: (data: ValueType) => void;
  value?: ValueType;
  defaultTime?: TimeType;
  pickerTimeChange?: () => void;
  showTime?: boolean;
  showTimeTool?: boolean;
  timeToolOptions?: timeToolOptions[];
}

export const getTimeByType = (type: TimeType) => {
  switch (type) {
    case TimeKey.hour:
      return moment().subtract(1, 'hours').valueOf();
    case TimeKey.week:
      return moment().subtract(6, 'days').valueOf();
    case TimeKey.month:
      return moment().subtract(29, 'days').valueOf();
    case TimeKey.year:
      return moment().subtract(365, 'days').valueOf();
    default:
      return moment().startOf('day').valueOf();
  }
};

export default forwardRef((props: ExtraTimePickerProps, ref) => {
  const [radioValue, setRadioValue] = useState<TimeType | undefined>(undefined);

  const { value, onChange, ...extraProps } = props;

  const change = (startTime: number, endTime: number, type: TimeType) => {
    if (onChange) {
      onChange({
        start: startTime,
        end: endTime,
        type,
      });
    }
  };

  const timeChange = (type: TimeType) => {
    let endTime = moment(new Date()).valueOf();
    let startTime: number = getTimeByType(type);
    if (type === TimeKey.yesterday) {
      startTime = moment().subtract(1, 'days').startOf('day').valueOf();
      endTime = moment().subtract(1, 'days').endOf('day').valueOf();
    }
    setRadioValue(type);
    change(startTime, endTime, type);
  };

  useImperativeHandle(ref, () => ({
    timeChange,
  }));

  useEffect(() => {
    timeChange(props.defaultTime || TimeKey.today);
  }, []);

  return (
    <>
      {
        // @ts-ignore
        <DatePicker.RangePicker
          {...extraProps}
          allowClear={false}
          showTime={props.showTime}
          value={
            value && [
              moment(value && value.start ? value.start : new Date()),
              moment(value && value.end ? value.end : new Date()),
            ]
          }
          onChange={(rangeValue) => {
            setRadioValue(undefined);
            if (rangeValue && rangeValue.length === 2) {
              change(rangeValue[0]!.valueOf(), rangeValue[1]!.valueOf(), undefined);
            }
            if (props.pickerTimeChange) {
              props.pickerTimeChange();
            }
          }}
          renderExtraFooter={
            props.showTimeTool !== true
              ? () => (
                  <div style={{ padding: '12px 0' }}>
                    <Radio.Group
                      defaultValue="day"
                      buttonStyle="solid"
                      value={radioValue}
                      onChange={(e) => {
                        timeChange(e.target.value);
                      }}
                    >
                      {props.timeToolOptions && Array.isArray(props.timeToolOptions) ? (
                        props.timeToolOptions.map((item) => (
                          <Radio.Button value={item.value}>{item.label}</Radio.Button>
                        ))
                      ) : (
                        <>
                          <Radio.Button value={TimeKey.today}>今日</Radio.Button>
                          <Radio.Button value={TimeKey.week}>近一周</Radio.Button>
                          <Radio.Button value={TimeKey.month}>近一月</Radio.Button>
                          <Radio.Button value={TimeKey.year}>近一年</Radio.Button>
                        </>
                      )}
                    </Radio.Group>
                  </div>
                )
              : undefined
          }
        />
      }
    </>
  );
});

上面是timepicker.tsx
 

import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import Style from './index.less';
import { Col, Form, Radio, Row } from 'antd';
import type { TimeType } from './timePicker';//引用了*&*&&*……&……


interface timeToolOptions {
  label: string;
  value: string;
}

export interface HeaderProps {
  title: string;
  extraParams?: {
    key: string;
    Children: React.ReactNode;
  };
  initialValues?: any;
  /**
   * true 关闭初始化时触发onParamsChange
   */
  closeInitialParams?: boolean;
  defaultTime?: TimeType & string;
  showTime?: boolean;
  showTimeTool?: boolean;
  timeToolOptions?: timeToolOptions[];
}

const NoTimeHeader=(props: HeaderProps) => {
  const [form] = Form.useForm();
  return (
    <div className={Style.header}>
      <div className={Style.title}>{props.title}</div>
      </div>
  );
};

export default NoTimeHeader;

上面是NoTimeHeader.tsx

import type { HeaderProps } from './noTimeHeader';//引用&&*…………(&((
import type { EchartsProps } from './echarts';
import Header from './noTimeHeader';//引用&……&……*&**&((
import Echarts from './echarts';
import Style from './index.less';
import classNames from 'classnames';
import { Empty } from '@/components';
import React, { forwardRef, useEffect, useState } from 'react';

interface BaseCardProps extends HeaderProps, EchartsProps {
  height: number;
  className?: string;
  echartsAfter?: React.ReactNode;
}

const NoTimeBaseCard=(props: BaseCardProps) => {
  const { height, className, options, ...formProps } = props;

  const [myOptions, setMyOptions] = useState(options);

  useEffect(() => {
    console.log('myOptions-change');
    setMyOptions(options);
  }, [options]);
  return (
    <div
      className={classNames(Style['dash-board'], className)}
      style={{
        height: height || 200,
      }}
    >
      <Header  {...formProps} />
      <div className={Style['echarts-content']}>
        {!!myOptions?.series?.[0]?.data?.length ? (
          <Echarts options={myOptions} className={Style['echarts']} />
        ) : (
          <Empty />
        )}
        {props.echartsAfter}
      </div>
    </div>
  );
};

export default NoTimeBaseCard;

上面是NoTimeBaseCard.tsx

import BaseCard from './baseCard';

export { default as DashBoardEcharts } from './echarts';
export { default as DashBoardHeader } from './header';
export { default as DashBoardTopCard } from './topCard';
export { default as DashBoardDeviceCard } from './deviceCard';
export { default as NoTimeBaseCard } from './noTimeBaseCard';

export default BaseCard;

上面是index.tsx

import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import Style from './index.less';
import { Col, Form, Radio, Row } from 'antd';
import type { TimeType } from './timePicker';           //&()()**()*
import RangePicker, { TimeKey } from './timePicker';//&*&(*(()*()(*

interface timeToolOptions {
  label: string;
  value: string;
}

export interface HeaderProps {
  title: string;
  /**
   * 参数发生变化时的回调
   * @param data
   */
  onParamsChange: (data: any) => void;
  extraParams?: {
    key: string;
    Children: React.ReactNode;
  };
  initialValues?: any;
  /**
   * true 关闭初始化时触发onParamsChange
   */
  closeInitialParams?: boolean;
  defaultTime?: TimeType & string;
  showTime?: boolean;
  showTimeTool?: boolean;
  timeToolOptions?: timeToolOptions[];
}

export default forwardRef((props: HeaderProps, ref) => {
  const [form] = Form.useForm();
  const [radioValue, setRadioValue] = useState<TimeType | undefined>(undefined);
  const isCloseInitial = useRef<boolean>(false);
  const pickerRef = useRef<any>(null);

  const change = async (data: any) => {
    if (props.onParamsChange) {
      props.onParamsChange(data);
    }
  };

  useImperativeHandle(ref, () => ({
    getValues: form.getFieldsValue,
  }));

  useEffect(() => {
    setRadioValue(props.defaultTime || TimeKey.today);
  }, []);

  return (
    <div className={Style.header}>
      <div className={Style.title}>{props.title}</div>
      <div className={Style.form}>
        <Form
          form={form}
          colon={false}
          layout={'inline'}
          preserve={false}
          initialValues={props.initialValues}
          onValuesChange={(_, allValue) => {
            console.log(allValue, 'allValue');
            if (props.closeInitialParams && !isCloseInitial.current) {
              isCloseInitial.current = true;
            } else {
              change(allValue);
            }
          }}
        >
          <Row style={{ width: '100%' }}>
            {props.extraParams && (
              <Col span={6}>
                <Form.Item name={props.extraParams.key}>{props.extraParams.Children}</Form.Item>
              </Col>
            )}
            <Col span={props.extraParams ? 18 : 24}>
              <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12 }}>
                {props.showTimeTool ? (
                  <Radio.Group
                    defaultValue="day"
                    buttonStyle="solid"
                    value={radioValue}
                    onChange={(e) => {
                      setRadioValue(e.target.value);
                      if (pickerRef.current) {
                        pickerRef.current.timeChange(e.target.value);
                      }
                    }}
                  >
                    {props.timeToolOptions && Array.isArray(props.timeToolOptions) ? (
                      props.timeToolOptions.map((item) => (
                        <Radio.Button value={item.value}>{item.label}</Radio.Button>
                      ))
                    ) : (
                      <>
                        <Radio.Button value={TimeKey.today}>今日</Radio.Button>
                        <Radio.Button value={TimeKey.week}>近一周</Radio.Button>
                        <Radio.Button value={TimeKey.month}>近一月</Radio.Button>
                        <Radio.Button value={TimeKey.year}>近一年</Radio.Button>
                      </>
                    )}
                  </Radio.Group>
                ) : null}
                <Form.Item noStyle name={'time'}>
                  <RangePicker
                    ref={pickerRef}
                    defaultTime={props.defaultTime}
                    timeToolOptions={props.timeToolOptions}
                    showTime={props.showTime}
                    showTimeTool={props.showTimeTool}
                    pickerTimeChange={() => {
                      setRadioValue(undefined);
                    }}
                  />
                </Form.Item>
              </div>
            </Col>
          </Row>
        </Form>
      </div>
    </div>
  );
});

上面是Header.tsx

import { useEffect, useRef, useState } from 'react';
import * as echarts from 'echarts/core';
import type { ECharts, EChartsOption } from 'echarts';
import {
  GridComponent,
  LegendComponent,
  MarkLineComponent,
  TitleComponent,
  ToolboxComponent,
  TooltipComponent,
} from 'echarts/components';

import { BarChart, LineChart, PieChart } from 'echarts/charts';

import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';

import Style from './index.less';
import classNames from 'classnames';

export interface EchartsProps {
  options?: EChartsOption;
  className?: string;
}

echarts.use([
  TitleComponent,
  ToolboxComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
  LineChart,
  CanvasRenderer,
  UniversalTransition,
  BarChart,
  MarkLineComponent,
  PieChart,
]);

const DefaultOptions = {
  xAxis: {
    type: 'category',
    data: [0],
  },
  yAxis: {
    type: 'value',
  },
  series: [
    {
      data: [],
      type: 'line',
    },
  ],
};

export { echarts };

export default (props: EchartsProps) => {
  const chartsRef = useRef<any>(null);
  const chartsDom = useRef<any>(null);
  const [loading, setLoading] = useState(false);

  const initEcharts = (dom: HTMLDivElement) => {
    console.log('------------init');
    if (!dom) return;
    chartsRef.current = chartsRef.current || echarts.init(dom);
    // chartsRef.current.clear()
    if (props.options) {
      chartsRef.current.setOption(props.options);
    } else {
      chartsRef.current.setOption(DefaultOptions);
    }
  };

  const updateSize = () => {
    if (chartsRef.current) {
      // 自适应屏幕变化
      (chartsRef.current as ECharts).resize();
    }
  };

  useEffect(() => {
    setTimeout(() => (window as Window).addEventListener('resize', updateSize), 100);
    return () => {
      (window as Window).removeEventListener('resize', updateSize);
      if (chartsRef.current) {
        // chartsRef.current.clear();
        chartsRef.current.dispose();
        chartsRef.current = null;
      }
    };
  }, []);

  useEffect(() => {
    if (chartsRef.current && props.options) {
      // chartsRef.current.clear()
      chartsRef.current.setOption(props.options);
    }
  }, [props.options, chartsRef.current]);

  useEffect(() => {
    // console.log('------------init')
    if (loading) {
      setTimeout(() => {
        initEcharts(chartsDom.current);
      }, 300);
    }
  }, [loading]);

  useEffect(() => {
    if (chartsDom.current) {
      setLoading(true);
    }
  }, [chartsDom.current]);

  return <div className={classNames(Style['content'], props.className)} ref={chartsDom} />;
};

上面是echarts.tsx

import React from 'react';
import { Badge, Col, Row } from 'antd';
import classNames from 'classnames';
import './index.less';

interface DeviceCardProps {
  children: React.ReactNode;
  className?: string;
  style?: React.CSSProperties;
}

interface FooterItem {
  status?: 'error' | 'success' | 'warning';
  title: string;
  value: string | number;
}

interface CardItemProps {
  span: number;
  title: string | React.ReactNode;
  value: any;
  showValue?: boolean;
  children: React.ReactNode;
}

const CardItem = (props: CardItemProps) => {
  return (
    <Col span={props.span}>
      <div className={'dash-board-top-item'}>
        <div
          className={classNames('top-item-content', { 'show-value': props.showValue !== false })}
        >
          <div className={'content-left'}>
            <div className={'content-left-title'}>{props.title}</div>
            {props.showValue !== false && <div className={'content-left-value'}>{props.value}</div>}
          </div>
          <div className={'content-right'}>{props.children}</div>
        </div>
      </div>
    </Col>
  );
};

const DeviceCard = (props: DeviceCardProps) => {
  return (
    <div className={classNames('dash-board-top', props.className)} style={props.style}>
      <Row gutter={24}>{props.children}</Row>
    </div>
  );
};

DeviceCard.Item = CardItem;

export default DeviceCard;

上面是DeviceCard

import type { HeaderProps } from './header';
import Header from './header';
import type { EchartsProps } from './echarts';
import Echarts from './echarts';
import Style from './index.less';
import classNames from 'classnames';
import React, { forwardRef, useEffect, useState } from 'react';
import { Empty } from '@/components';

interface BaseCardProps extends HeaderProps, EchartsProps {
  height: number;
  className?: string;
  echartsAfter?: React.ReactNode;
}

export default forwardRef((props: BaseCardProps, ref) => {
  const { height, className, options, ...formProps } = props;

  const [myOptions, setMyOptions] = useState(options);

  useEffect(() => {
    console.log('myOptions-change');
    setMyOptions(options);
  }, [options]);
  return (
    <div
      className={classNames(Style['dash-board'], className)}
      style={{
        height: height || 200,
      }}
    >
      <Header ref={ref} {...formProps} />
      <div className={Style['echarts-content']}>
        {!!myOptions?.series?.[0]?.data?.length ? (
          <Echarts options={myOptions} className={Style['echarts']} />
        ) : (
          <Empty />
        )}
        {props.echartsAfter}
      </div>
    </div>
  );
});

上面是BaseCard

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GuoGei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值