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

ReactJS结合WEB API实现批量员工数据导入进度条方案咨询

实现批量员工数据提交的实时进度展示方案

针对批量提交数千条员工数据后无法感知后端处理进度的问题,结合你使用的React和C#后端场景,提供三个实用的实现方案:


方案1:拆分批量请求,前端自主计算进度

如果后端不想做过多改动,这是最快速的实现方式——把大数组拆分成小批次提交,前端根据已完成的批次占比计算进度。

前端实现步骤:

  1. 安装你提到的进度条组件:
npm install @ramonak/react-progress-bar
  1. React组件内的完整逻辑:
import { useState } from 'react';
import ProgressBar from '@ramonak/react-progress-bar';

const EmployeeUpload = () => {
  const [progress, setProgress] = useState(0);
  const [isUploading, setIsUploading] = useState(false);
  const usr = '你的用户Token'; // 替换为实际Token

  // 将数据拆分为指定大小的批次
  const splitIntoBatches = (data, batchSize) => {
    const batches = [];
    for (let i = 0; i < data.length; i += batchSize) {
      batches.push(data.slice(i, i + batchSize));
    }
    return batches;
  };

  const saveEmployees = async (empData) => {
    setIsUploading(true);
    setProgress(0);
    const batches = splitIntoBatches(empData, 50); // 可根据后端性能调整批次大小
    const totalBatches = batches.length;

    for (let i = 0; i < totalBatches; i++) {
      const batch = batches[i];
      const requestOptions = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          Token: usr,
          EmpData: batch
        })
      };

      await fetch('http://localhost:5000/api/services/app/Employee/AddEmployees', requestOptions);
      // 计算并更新进度
      setProgress(Math.round(((i + 1) / totalBatches) * 100));
    }

    setIsUploading(false);
    alert('数据提交完成');
  };

  // 示例员工数据,替换为你的实际数据
  const empData = [
    { id: 5495, employeeName: "Sarwen", department: "Production", factoryLocation: "Denmark" },
    { id: 5496, employeeName: "Kate", department: "HR", factoryLocation: "Denmark" }
  ];

  return (
    <div>
      <button onClick={() => saveEmployees(empData)} disabled={isUploading}>
        {isUploading ? '提交中...' : '批量提交员工数据'}
      </button>
      {isUploading && (
        <ProgressBar
          completed={progress}
          bgColor="#36a2eb"
          labelColor="#fff"
          height="20px"
          width="100%"
        />
      )}
    </div>
  );
};

export default EmployeeUpload;

方案2:后端提供进度查询接口,前端轮询获取进度

如果必须一次性提交所有数据给后端,需要后端配合添加进度追踪能力:

后端修改:

  1. 定义进度追踪模型和缓存(生产环境建议用Redis替代内存缓存):
// 进度追踪模型
public class ProcessProgress
{
    public string TaskId { get; set; }
    public int TotalCount { get; set; }
    public int ProcessedCount { get; set; }
    public bool IsCompleted { get; set; }
}

// 内存缓存容器(多服务器部署需替换为分布式缓存)
public static class ProgressCache
{
    public static Dictionary<string, ProcessProgress> Progresses = new Dictionary<string, ProcessProgress>();
}
  1. 修改原AddEmployees接口,返回任务ID并异步处理数据更新进度:
public async Task<string> AddEmployees(InputDto input)
{
    var taskId = Guid.NewGuid().ToString();
    var totalCount = input.EmpData.Length;
    // 初始化进度记录
    ProgressCache.Progresses[taskId] = new ProcessProgress
    {
        TaskId = taskId,
        TotalCount = totalCount,
        ProcessedCount = 0,
        IsCompleted = false
    };

    // 异步处理数据,避免阻塞接口返回
    _ = Task.Run(async () =>
    {
        foreach (var emp in input.EmpData)
        {
            // 执行数据库插入逻辑
            // await _dbContext.Employees.AddAsync(emp);
            // await _dbContext.SaveChangesAsync();

            // 更新已处理数量
            ProgressCache.Progresses[taskId].ProcessedCount++;
            await Task.Delay(10); // 模拟数据库操作耗时
        }
        ProgressCache.Progresses[taskId].IsCompleted = true;
    });

    // 立即返回任务ID,供前端查询进度
    return taskId;
}
  1. 新增进度查询接口:
public IActionResult GetProcessProgress(string taskId)
{
    if (ProgressCache.Progresses.TryGetValue(taskId, out var progress))
    {
        return Ok(new
        {
            progress.TotalCount,
            progress.ProcessedCount,
            progress.IsCompleted,
            Percentage = (int)Math.Round(((double)progress.ProcessedCount / progress.TotalCount) * 100)
        });
    }
    return NotFound("任务不存在");
}

前端实现:

import { useState, useEffect } from 'react';
import ProgressBar from '@ramonak/react-progress-bar';

const EmployeeUpload = () => {
  const [progress, setProgress] = useState(0);
  const [isUploading, setIsUploading] = useState(false);
  const [taskId, setTaskId] = useState('');
  const usr = '你的用户Token';

  const saveEmployees = async (empData) => {
    setIsUploading(true);
    setProgress(0);
    const requestOptions = {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        Token: usr,
        EmpData: empData
      })
    };

    // 提交数据并获取任务ID
    const response = await fetch('http://localhost:5000/api/services/app/Employee/AddEmployees', requestOptions);
    const newTaskId = await response.text();
    setTaskId(newTaskId);
  };

  // 轮询进度接口
  useEffect(() => {
    let interval;
    if (taskId && isUploading) {
      interval = setInterval(async () => {
        const response = await fetch(`http://localhost:5000/api/services/app/Employee/GetProcessProgress?taskId=${taskId}`);
        const progressData = await response.json();
        
        setProgress(progressData.Percentage);
        
        if (progressData.IsCompleted) {
          clearInterval(interval);
          setIsUploading(false);
          alert('数据处理完成');
          // 可选:通知后端清理任务缓存
        }
      }, 500); // 每500ms查询一次,可调整频率
    }
    return () => clearInterval(interval);
  }, [taskId, isUploading]);

  // 示例员工数据
  const empData = [
    { id: 5495, employeeName: "Sarwen", department: "Production", factoryLocation: "Denmark" },
    { id: 5496, employeeName: "Kate", department: "HR", factoryLocation: "Denmark" }
  ];

  return (
    <div>
      <button onClick={() => saveEmployees(empData)} disabled={isUploading}>
        {isUploading ? '处理中...' : '批量提交员工数据'}
      </button>
      {isUploading && (
        <ProgressBar
          completed={progress}
          bgColor="#36a2eb"
          labelColor="#fff"
          height="20px"
          width="100%"
        />
      )}
    </div>
  );
};

export default EmployeeUpload;

方案3:使用Server-Sent Events(SSE)实现实时进度推送

如果需要更实时的进度更新,可采用SSE让后端主动向前端推送进度:

后端SSE接口示例:

public async Task GetProcessProgressSSE(string taskId, HttpContext context)
{
    context.Response.Headers.Add("Content-Type", "text/event-stream");
    context.Response.Headers.Add("Cache-Control", "no-cache");
    context.Response.Headers.Add("Connection", "keep-alive");

    while (true)
    {
        if (!ProgressCache.Progresses.TryGetValue(taskId, out var progress))
        {
            await context.Response.WriteAsync("event: error\ndata: 任务不存在\n\n");
            await context.Response.Body.FlushAsync();
            break;
        }

        if (progress.IsCompleted)
        {
            await context.Response.WriteAsync($"event: complete\ndata: {JsonSerializer.Serialize(progress)}\n\n");
            await context.Response.Body.FlushAsync();
            break;
        }

        // 推送当前进度
        await context.Response.WriteAsync($"event: progress\ndata: {JsonSerializer.Serialize(progress)}\n\n");
        await context.Response.Body.FlushAsync();
        
        await Task.Delay(500);
    }
}

前端SSE实现:

import { useState } from 'react';
import ProgressBar from '@ramonak/react-progress-bar';

const EmployeeUpload = () => {
  const [progress, setProgress] = useState(0);
  const [isUploading, setIsUploading] = useState(false);
  const usr = '你的用户Token';

  const saveEmployees = async (empData) => {
    setIsUploading(true);
    setProgress(0);
    const requestOptions = {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        Token: usr,
        EmpData: empData
      })
    };

    const response = await fetch('http://localhost:5000/api/services/app/Employee/AddEmployees', requestOptions);
    const taskId = await response.text();

    // 建立SSE连接
    const eventSource = new EventSource(`http://localhost:5000/api/services/app/Employee/GetProcessProgressSSE?taskId=${taskId}`);
    
    eventSource.addEventListener('progress', (e) => {
      const progressData = JSON.parse(e.data);
      const percentage = Math.round(((progressData.ProcessedCount / progressData.TotalCount) * 100));
      setProgress(percentage);
    });

    eventSource.addEventListener('complete', () => {
      setIsUploading(false);
      eventSource.close();
      alert('数据处理完成');
    });

    eventSource.addEventListener('error', () => {
      eventSource.close();
      setIsUploading(false);
      alert('进度获取失败');
    });
  };

  // 示例员工数据
  const empData = [
    { id: 5495, employeeName: "Sarwen", department: "Production", factoryLocation: "Denmark" },
    { id: 5496, employeeName: "Kate", department: "HR", factoryLocation: "Denmark" }
  ];

  return (
    <div>
      <button onClick={() => saveEmployees(empData)} disabled={isUploading}>
        {isUploading ? '处理中...' : '批量提交员工数据'}
      </button>
      {isUploading && (
        <ProgressBar
          completed={progress}
          bgColor="#36a2eb"
          labelColor="#fff"
          height="20px"
          width="100%"
        />
      )}
    </div>
  );
};

export default EmployeeUpload;

额外说明:

  • 方案1适合快速落地,但需考虑单批次失败后的重试逻辑
  • 方案2的内存缓存仅适用于单服务器部署,生产环境建议用Redis等分布式缓存
  • 方案3的SSE是单向通信,若需要前端控制任务(暂停/取消),可改用WebSocket

内容的提问来源于stack exchange,提问作者weeraa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 05:24:11