批量导入模态框组件

组件样式


组件代码

import { Button, Input, message, Modal, Space, Spin, Upload } from 'antd'
import { useState } from 'react'
import { UploadOutlined, LoadingOutlined } from '@ant-design/icons'

// 定义批量导入模态框的属性接口
interface BatchModalProps {
  visible: boolean // 模态框是否可见
  handleCancel: () => void // 关闭模态框的回调
  handleOk: (data: LooseObject) => void // 确认导入成功的回调
  title: string // 模态框标题
  templateUrl: string // 模板下载链接
  maxSize?: number // 最大支持的条目数,默认为500
  onConfirmImport: (file: File) => Promise<LooseObject> // 确认导入的逻辑
}

/**
 * 批量导入模态框组件
 * @param props - 组件属性
 * @returns React 元素
 */
const BatchModal = (props: BatchModalProps) => {
  const {
    visible,
    handleCancel,
    handleOk,
    title,
    templateUrl,
    onConfirmImport,
    maxSize = 500
  } = props

  // 定义模态框内部状态
  const [formLoading, setFormLoading] = useState(false) // 表单加载状态
  const [uploadFile, setUploadFile] = useState<File | null>(null) // 上传的文件
  const [errorUrl, setErrorUrl] = useState('') // 错误数据下载链接
  const [loadStatus, setLoadStatus] = useState(1) // 加载状态:1-确认导入,2-正在导入
  const [loadError, setLoadError] = useState<number | string>('0') // 导入结果:0-全部成功,1-全部失败,2-部分成功

  /**
   * 确认导入文件
   */
  const onUploadFile = async () => {
    try {
      setLoadStatus(2) // 设置为正在导入状态
      const data = await onConfirmImport(uploadFile!) // 调用外界传入的确认导入逻辑
      if (data) {
        // 根据导入结果显示不同的消息
        if (data?.successUrl && !data?.errorUrl) {
          message.success('上传成功')
          handleCancel() // 关闭模态框
        }
        if (!data?.successUrl && data?.errorUrl) {
          message.error('上传失败')
        }
        if (data?.successUrl && data?.errorUrl) {
          message.error('上传部分成功')
        }
        // 设置导入结果和错误数据下载链接
        setLoadError(
          data?.successUrl && !data?.errorUrl
            ? '0'
            : !data?.successUrl && data?.errorUrl
            ? '1'
            : '2'
        )
        setErrorUrl(data.errorUrl)
        // 调用父组件的确认回调
        handleOk(data)
      }
      setLoadStatus(1) // 恢复为确认导入状态
    } catch (error) {
      // 捕获异常并恢复为确认导入状态
      console.error('导入失败', error)
      setLoadStatus(1)
    }
  }

  /**
   * 关闭模态框
   */
  const onCancel = () => {
    handleCancel() // 调用父组件的关闭回调
  }

  return (
    <>
      <Modal
        open={visible} // 模态框是否可见
        width={700} // 模态框宽度
        title={title} // 模态框标题
        footer={null} // 不显示默认的底部按钮
        onCancel={onCancel} // 关闭模态框的回调
        destroyOnClose // 关闭时销毁模态框内容
      >
        {loadStatus === 2 ? (
          // 正在导入状态
          <div style={{ textAlign: 'center' }}>
            <LoadingOutlined style={{ fontSize: 32, marginBottom: 16 }} />{' '}
            <br />
            导入中,请稍等!
          </div>
        ) : (
          // 确认导入状态
          <Spin spinning={formLoading}>
            <Space align="center" style={{ marginLeft: 64 }}>
              <span style={{ color: '#ff6062' }}>*</span>选择文件
              <Input
                value={uploadFile?.name} // 显示上传文件的名称
                readOnly // 只读输入框
                style={{ width: 240 }}
              />
              <Upload
                accept=".xls,.xlsx" // 限制上传文件类型
                showUploadList={false} // 不显示上传列表
                beforeUpload={(file) => {
                  // 在上传前处理文件
                  setLoadError('0') // 重置导入结果
                  setUploadFile(file) // 设置上传的文件
                  setErrorUrl('') // 重置错误数据下载链接
                  return false // 阻止默认上传行为
                }}>
                <Button icon={<UploadOutlined />}>浏览</Button>
              </Upload>
              <Button
                type="primary" // 主按钮样式
                disabled={!uploadFile} // 如果没有上传文件,禁用按钮
                onClick={onUploadFile} // 点击按钮时确认导入
              >
                确认导入
              </Button>
            </Space>
            <div
              style={{
                display: 'flex',
                justifyContent: 'space-between',
                margin: '15px 85px 15px 130px',
                boxSizing: 'border-box'
              }}>
              <div
                style={{
                  display: 'inline-block',
                  textAlign: 'center',
                  color: 'red'
                }}>
                <a download="导入模板.xlsx" href={templateUrl}>
                  点击下载导入模板
                </a>
                &nbsp;&nbsp;最大支持{maxSize}条!
              </div>
            </div>
            {loadError === '1' || loadError === '2' ? (
              // 如果导入失败或部分成功,显示错误信息和下载链接
              <div style={{ textAlign: 'center', marginTop: 30 }}>
                <p style={{ color: 'red' }}>
                  {loadError === '2'
                    ? '部分导入失败信息'
                    : loadError === '1'
                    ? '全部导入失败'
                    : ''}
                </p>
                <a href={errorUrl} rel="noreferrer" download="">
                  下载导入异常数据
                </a>
              </div>
            ) : null}
          </Spin>
        )}
      </Modal>
    </>
  )
}

export default BatchModal

文档说明

import type { Meta, StoryObj } from '@storybook/react'
import BatchModal from '../../components/BatchModal'

// 定义组件元数据
const meta = {
  title: '文件上传/BatchModal', // 组件标题
  component: BatchModal, // 组件
  argTypes: {
    visible: {
      control: { type: 'boolean' }, // visible 属性的类型为布尔值
      description: '模态框是否可见' // visible 属性的描述
    },
    handleCancel: {
      action: 'handleCancel', // handleCancel 属性的事件处理
      description: '关闭模态框的回调' // handleCancel 属性的描述
    },
    handleOk: {
      action: 'handleOk', // handleOk 属性的事件处理
      description: '确认导入成功的回调' // handleOk 属性的描述
    },
    title: {
      control: { type: 'text' }, // title 属性的类型为文本
      description: '模态框标题' // title 属性的描述
    },
    templateUrl: {
      control: { type: 'text' }, // templateUrl 属性的类型为文本
      description: '模板下载链接' // templateUrl 属性的描述
    },
    maxSize: {
      control: { type: 'number' }, // maxSize 属性的类型为数字
      description: '最大支持的条目数' // maxSize 属性的描述
    },
    onConfirmImport: {
      description: '确认导入的逻辑' // onConfirmImport 属性的描述
    }
  }
} satisfies Meta<typeof BatchModal>

export default meta
type Story = StoryObj<typeof meta>

// 基本用法示例
export const BasicUsage: Story = {
  args: {
    visible: true,
    title: '批量导入',
    templateUrl: 'https://example.com/success.xlsx',
    maxSize: 500,
    onConfirmImport: async (file) => {
      // 模拟确认导入逻辑
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            successUrl: 'https://example.com/success.xlsx',
            errorUrl: 'https://example.com/error.xlsx'
          })
        }, 1000)
      })
    }
  }
}

// 自定义配置示例
export const CustomConfiguration: Story = {
  args: {
    visible: true,
    title: '自定义批量导入',
    templateUrl: 'https://example.com/success.xlsx',
    maxSize: 1000,
    onConfirmImport: async (file) => {
      // 模拟确认导入逻辑
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            successUrl: 'https://example.com/success.xlsx',
            errorUrl: 'https://example.com/error.xlsx'
          })
        }, 1000)
      })
    }
  }
}

// 导入状态示例
export const ImportStatus: Story = {
  args: {
    visible: true,
    title: '导入状态显示',
    templateUrl: 'https://example.com/success.xlsx',
    maxSize: 500,
    onConfirmImport: async (file) => {
      // 模拟确认导入逻辑
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            successUrl: 'https://example.com/success.xlsx'
            // errorUrl: 'https://example.com/error.xlsx'
          })
        }, 1000)
      })
    }
  }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值