You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为React统计页面添加全局Spinner Loader?

如何为Dashboard页面添加全局Spinner Loader

我有一个包含多个界面的项目,其中有一个展示各类统计数据的界面,该界面中每个图表的数据都通过对应API获取。现在需要为这个页面添加全局Spinner Loader,请问该如何实现?以下是负责展示所有统计内容的Dashboard组件代码:

import { Card, Col, Row, Spin } from 'antd';
import { FunctionComponent, useContext, useEffect, useState } from 'react';
import '../../../styles/dashboard/index.scss';
import { AuthContext, IAuthContext } from '../../../contexts/auth-context';
import colors from '../../../constants/colors';
import calendar1 from '../../../assets/icons/calendar1-icon.svg';
import waving from '../../../assets/icons/waving-hand-icon.svg';
import { FormattedMessage } from 'react-intl';
import BarCharts from './charts/barCharts';
import PieCharts from './charts/pieCharts';
import { useQuery } from "react-query";
import statisticCharts from '../../../api/nuclearMedicineApi/services/Statistic';
import { ChartEntityType1, ChartEntityType2, ChartEntityType3, ChartEntityTypeAnalysis } from './types';
import schedule from "../../../api/nuclearMedicineApi/services/Schedule";
import { MachineCategory } from '../../../constants/enums';


interface DashboardProps { }
const Dashboard: FunctionComponent<DashboardProps> = () => {

    const current = new Date();
    const date = `${current.getFullYear()}/${current.getMonth() + 1
        }/${current.getDate()}`;


    const auth = useContext<IAuthContext>(AuthContext);

    const today = new Date();
    var year = today.getFullYear();

    const [xytotalTopography, setxytotalTopography] = useState<any[]>([]);
    const [xtopography, setXTopography] = useState<any[]>([]);
    const [xyAge, setxyAge] = useState<any[]>([]);
    const [xyCitytotalTopography, setxyCitytotalTopography] = useState<any[]>([]);
    const [xyTopographyPercent, setxyTopographyPercent] = useState<any[]>([]);

    const [analysisCountPatient, setAnalysisCountPatient] = useState<any[]>([]);
    const [ctCountPatient, setCtCountPatient] = useState<any[]>([]);
    const [mriCountPatient, setMriCountPatient] = useState<any[]>([]);
    const [gammaCountPatient, setGammaCountPatient] = useState<any[]>([]);
    const [xrayCountPatient, setXrayCountPatient] = useState<any[]>([]);
    const [cesiumCountPatient, setCesiumCountPatient] = useState<any[]>([]);
    const [cobaltCountPatient, setCobaltCountPatient] = useState<any[]>([]);
    const [iodinCountPatient, setIodinCountPatient] = useState<any[]>([]);
    const [linacCountPatient, setLinacCountPatient] = useState<any[]>([]);


    const chartsQueryTopographyData = useQuery(['chartsQueryTopographyData'], () =>
        statisticCharts.GetTopographyStatistic({ FilterType: 1, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
    ).data;

    const chartsQueryCityData = useQuery(['chartsQueryCityData'], () =>
        statisticCharts.GetTopographyStatistic({ FilterType: 2, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
    ).data;

    const chartsQueryAgeData = useQuery(['chartsQueryAgeData'], () =>
        statisticCharts.GetTopographyStatistic({ FilterType: 3, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
    ).data;

    // count
    const schedulePatientCount = useQuery('schedulePatientCount', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.Analysiss,
        }),
    ).data;

    const schedulePatientCountiodin = useQuery('schedulePatientCountiodin', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadioTherapyByIodine,
        }),
    ).data;

    const schedulePatientCountCt = useQuery('schedulePatientCountCt', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadialImageByCT,
        }),
    ).data;

    const schedulePatientCountGamma = useQuery('schedulePatientCountGamma', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadialImageByGamma,
        }),
    ).data;

    const schedulePatientCountMri = useQuery('schedulePatientCountMri', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadialImageByMRI,
        }),
    ).data;

    const schedulePatientCountXray = useQuery('schedulePatientCountXray', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadialImageByXRay,
        }),
    ).data;

    const schedulePatientCountcesium = useQuery('schedulePatientCountcesium', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadioTherapyByCesium,
        }),
    ).data;

    const schedulePatientCountCobalt = useQuery('schedulePatientCountCobalt', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadioTherapyByCobalt,
        }),
    ).data;

    const schedulePatientCountLinac = useQuery('schedulePatientCountLinac', () =>
        schedule.SchedulePatientCountGetAllLite({
            machineCategory: MachineCategory.RadioTherapyByLinac,
        }),
    ).data;

    // *********** this function return array of arrays
    const cleanData1 = (data: ChartEntityType1[]) =>
        data?.map((xy: ChartEntityType1) => {
            if (xy?.x !== undefined && xy?.yMale !== undefined && xy?.yFemale !== undefined) return [xy?.x, xy?.yMale, xy?.yFemale]
        });


    const cleanData2 = (data: ChartEntityType2[]) =>
        data?.map((xy: ChartEntityType2) => {
            if (xy?.x !== undefined && xy?.yTotal !== undefined) return [xy?.x, xy?.yTotal]
        });


    const cleanData3 = (data: ChartEntityType3[]) =>
        data?.map((xy: ChartEntityType3) => {
            if (xy?.x !== undefined && xy?.yPercent !== undefined) return [xy?.x, xy?.yPercent]
        });

    const cleanData4 = (data: ChartEntityTypeAnalysis[]) =>
        data?.map((xy: ChartEntityTypeAnalysis) => {
            if (xy !== undefined) if (xy?.count !== undefined && xy?.machine?.label !== undefined)
                return [xy?.machine?.label, xy?.count]
        });


    useEffect(() => {
        // x = topography, y= male, y1= female
        // *************** this return array of arrays
        const mappedData1 = cleanData1(chartsQueryTopographyData);
        const mappedData2 = cleanData2(chartsQueryTopographyData);
        const mappedData3 = cleanData2(chartsQueryAgeData);
        const mappedData4 = cleanData3(chartsQueryCityData);
        const mappedData5 = cleanData3(chartsQueryTopographyData);
        const mappedData6 = cleanData4(schedulePatientCount);

        const mappedData7 = cleanData4(schedulePatientCountCt);
        const mappedData8 = cleanData4(schedulePatientCountGamma);
        const mappedData9 = cleanData4(schedulePatientCountMri);
        const mappedData10 = cleanData4(schedulePatientCountXray);
        const mappedData11 = cleanData4(schedulePatientCountCobalt);
        const mappedData12 = cleanData4(schedulePatientCountcesium);
        const mappedData13 = cleanData4(schedulePatientCountiodin);
        const mappedData14 = cleanData4(schedulePatientCountLinac);

        if (mappedData1) {
            setxytotalTopography([['topography', 'ذكر', 'أنثى'], ...mappedData1])
        }

        if (mappedData2) {
            setXTopography([['topography', 'المرضى المصابين'], ...mappedData2])
        }

        if (mappedData3) {
            setxyAge([['age', 'المرضى المصابين'], ...mappedData3])
        }

        if (mappedData4) {
            setxyCitytotalTopography([['', ''], ...mappedData4])
        }

        if (mappedData5) {
            setxyTopographyPercent([['', ''], ...mappedData5])
        }

        if (mappedData6) {
            setAnalysisCountPatient([['', ''], ...mappedData6])
        }

        if (mappedData7) {
            setCtCountPatient([['', ''], ...mappedData7])
        }
        if (mappedData8) {
            setGammaCountPatient([['', ''], ...mappedData8])
        }
        if (mappedData9) {
            setMriCountPatient([['', ''], ...mappedData9])
        }

        if (mappedData10) {
            setXrayCountPatient([['', ''], ...mappedData10])
        }

        if (mappedData11) {
            setCobaltCountPatient([['', ''], ...mappedData11])
        }
        if (mappedData12) {
            setCesiumCountPatient([['', ''], ...mappedData12])
        }
        if (mappedData13) {
            setIodinCountPatient([['', ''], ...mappedData13])
        }
        if (mappedData14) {
            setLinacCountPatient([['', ''], ...mappedData14])
        }
    }, [chartsQueryTopographyData, chartsQueryCityData, chartsQueryAgeData, schedulePatientCount,
        schedulePatientCountCt, schedulePatientCountGamma, schedulePatientCountMri, schedulePatientCountXray,
        schedulePatientCountCobalt, schedulePatientCountcesium, schedulePatientCountiodin, schedulePatientCountLinac]);


    return (
        <>
            <div className='dashdoard-donutData'>
                {/* welcome */}
                <Row>
                    <Card className='card-welcome'>
                        <Row className='center'>
                            <Col lg={20}>

                                <Row>
                                    <Col>
                                        <FormattedMessage id='welcome' />
                                        {'  '}
                                        {auth.userData?.fullName} !
                                    </Col>
                                    <Col className='mr-2 ml-2'>
                                        <img src={waving} alt='waving' />
                                    </Col>
                                    <Col>
                                        <small>
                                            <FormattedMessage id='follow' />
                                        </small>
                                    </Col>
                                </Row>
                            </Col>
                            <Col lg={4}>
                                <Row
                                    style={{
                                        color: colors.primaryColor,
                                    }}
                                    justify='end'
                                >
                                    <Col>
                                        <img
                                            src={calendar1}
                                            alt='calendar1'
                                        />
                                    </Col>
                                    <Col>
                                        {' '}
                                        {'\u00a0\u00a0'}{' '}
                                        <FormattedMessage id='today' />
                                        {date}
                                    </Col>
                                </Row>
                            </Col>
                        </Row>
                    </Card>
                </Row>

                {/* Close Date Appointment*/}
                {/* <ClosestFreeAppointment /> */}

                {/* Bar Charts */}
                {/* <BarCharts topographyData={chartsQueryTopographyData?.data} ageData={chartsQueryAgeData?.data} year={year} /> */}
                <BarCharts xytotalTopography={xytotalTopography} xtopography={xtopography} xyAge={xyAge} />

                {/* Pie Charts */}
                <PieCharts xyCitytotalTopography={xyCitytotalTopography}
                    xyTopographyPercent={xyTopographyPercent}
                    analysisCountPatient={analysisCountPatient}
                    ctCountPatient={ctCountPatient}
                    mriCountPatient={mriCountPatient}
                    gammaCountPatient={gammaCountPatient}
                    xrayCountPatient={xrayCountPatient}
                    cesiumCountPatient={cesiumCountPatient}
                    cobaltCountPatient={cobaltCountPatient}
                    iodinCountPatient={iodinCountPatient}
                    linacCountPatient={linacCountPatient}
                    year={year} />
            </div>
        </>
    );
};

export default Dashboard;

实现方案

你的项目使用了React Query管理API请求,直接利用它提供的加载状态就能实现全局Spinner,无需额外维护复杂状态,具体步骤如下:

1. 保留完整的useQuery返回对象

之前你只提取了.data,现在需要保留每个useQuery的完整对象,这样才能获取isLoading状态:

// 替换原只取.data的写法,保留完整query对象
const chartsQueryTopographyDataQuery = useQuery(['chartsQueryTopographyData'], () =>
  statisticCharts.GetTopographyStatistic({ FilterType: 1, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
);

const chartsQueryCityDataQuery = useQuery(['chartsQueryCityData'], () =>
  statisticCharts.GetTopographyStatistic({ FilterType: 2, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
);

const chartsQueryAgeDataQuery = useQuery(['chartsQueryAgeData'], () =>
  statisticCharts.GetTopographyStatistic({ FilterType: 3, StartDate: `${year}-01-01`, EndDate: `${year}-12-31` }),
);

// 所有count相关的useQuery做同样修改
const schedulePatientCountQuery = useQuery('schedulePatientCount', () =>
  schedule.SchedulePatientCountGetAllLite({
    machineCategory: MachineCategory.Analysiss,
  }),
);

const schedulePatientCountiodinQuery = useQuery('schedulePatientCountiodin', () =>
  schedule.SchedulePatientCountGetAllLite({
    machineCategory: MachineCategory.RadioTherapyByIodine,
  }),
);

// ... 其他count的useQuery按此方式修改

2. 计算全局加载状态

创建变量判断所有API请求是否完成加载:

// 检查所有查询是否处于加载状态
const isGlobalLoading = 
  chartsQueryTopographyDataQuery.isLoading ||
  chartsQueryCityDataQuery.isLoading ||
  chartsQueryAgeDataQuery.isLoading ||
  schedulePatientCountQuery.isLoading ||
  schedulePatientCountiodinQuery.isLoading ||
  schedulePatientCountCtQuery.isLoading ||
  schedulePatientCountGammaQuery.isLoading ||
  schedulePatientCountMriQuery.isLoading ||
  schedulePatientCountXrayQuery.isLoading ||
  schedulePatientCountcesiumQuery.isLoading ||
  schedulePatientCountCobaltQuery.isLoading ||
  schedulePatientCountLinacQuery.isLoading;

3. 用Antd Spin组件包裹页面内容

将整个Dashboard内容用Spin组件包裹,根据isGlobalLoading控制显示:

return (
  <Spin spinning={isGlobalLoading} tip="数据加载中...">
    <div className='dashdoard-donutData'>
      {/* 原页面所有内容:欢迎卡片、BarCharts、PieCharts等 */}
      <Row>
        <Card className='card-welcome'>
          {/* ... 原欢迎卡片内容 */}
        </Card>
      </Row>

      <BarCharts 
        xytotalTopography={xytotalTopography} 
        xtopography={xtopography} 
        xyAge={xyAge} 
      />

      <PieCharts 
        xyCitytotalTopography={xyCitytotalTopography}
        xyTopographyPercent={xyTopographyPercent}
        analysisCountPatient={analysisCountPatient}
        ctCountPatient={ctCountPatient}
        mriCountPatient={mriCountPatient}
        gammaCountPatient={gammaCountPatient}
        xrayCountPatient={xrayCountPatient}
        cesiumCountPatient={cesiumCountPatient}
        cobaltCountPatient={cobaltCountPatient}
        iodinCountPatient={iodinCountPatient}
        linacCountPatient={linacCountPatient}
        year={year} 
      />
    </div>
  </Spin>
);

4. 调整数据获取逻辑

因为现在保留的是完整query对象,后续使用数据时要改为从.data属性获取:

useEffect(() => {
  const mappedData1 = cleanData1(chartsQueryTopographyDataQuery.data);
  const mappedData2 = cleanData2(chartsQueryTopographyDataQuery.data);
  const mappedData3 = cleanData2(chartsQueryAgeDataQuery.data);
  const mappedData4 = cleanData3(chartsQueryCityDataQuery.data);
  const mappedData5 = cleanData3(chartsQueryTopographyDataQuery
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 08:48:29