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

如何为SPFx解决方案(Web部件)创建附件组件?

SPFx 自定义附件组件实现方案

由于Office UI Fabric没有原生的附件上传组件,下面提供一个适配SPFx Web部件的自定义附件组件,和你已有的TextFieldInput组件风格保持一致,支持文件选择、预览、移除等核心功能:

附件组件代码

import * as React from 'react';
import * as PropTypes from 'prop-types';
import { Button, List, Icon, Tooltip } from 'office-ui-fabric-react';
import { getFileSizeString } from '@microsoft/sp-core-library';

const AttachmentInput = ({ props, otherProps = null, onFilesChange }) => {
  const fileInputRef = React.useRef<HTMLInputElement>(null);
  const [selectedFiles, setSelectedFiles] = React.useState<File[]>([]);

  // 处理文件选择事件
  const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (!files) return;

    const newFiles = Array.from(files);
    // 若允许重复添加则合并,否则替换现有列表
    const updatedFiles = props.allowDuplicate ? [...selectedFiles, ...newFiles] : newFiles;
    setSelectedFiles(updatedFiles);
    onFilesChange(updatedFiles);
  };

  // 移除单个文件
  const handleRemoveFile = (index: number) => {
    const updatedFiles = selectedFiles.filter((_, i) => i !== index);
    setSelectedFiles(updatedFiles);
    onFilesChange(updatedFiles);
  };

  // 触发文件选择框
  const triggerFileInput = () => {
    fileInputRef.current?.click();
  };

  // 渲染单个文件项
  const renderFileItem = (item: File, index: number) => (
    <div key={index} style={{ display: 'flex', alignItems: 'center', padding: '8px 0', borderBottom: '1px solid #eee' }}>
      <Icon iconName="Document" style={{ fontSize: 24, marginRight: 8 }} />
      <div style={{ flexGrow: 1 }}>
        <div style={{ fontWeight: 500 }}>{item.name}</div>
        <div style={{ fontSize: 12, color: '#666' }}>{getFileSizeString(item.size)}</div>
      </div>
      <Tooltip content="移除文件">
        <Button
          iconName="Delete"
          onClick={() => handleRemoveFile(index)}
          disabled={props.disabled}
          styles={{ root: { padding: '4px' } }}
        />
      </Tooltip>
    </div>
  );

  return (
    <div className={props.class ? props.class : ""} {...otherProps}>
      {/* 隐藏原生文件输入框 */}
      <input
        ref={fileInputRef}
        type="file"
        multiple={props.multiple}
        accept={props.accept}
        disabled={props.disabled}
        onChange={handleFileChange}
        style={{ display: 'none' }}
      />

      {/* 触发按钮 */}
      <Button
        iconName="Upload"
        onClick={triggerFileInput}
        disabled={props.disabled}
        text={props.buttonText ? props.buttonText : "添加附件"}
      />

      {/* 已选文件列表 */}
      {selectedFiles.length > 0 && (
        <div style={{ marginTop: 16 }}>
          <div style={{ marginBottom: 8, fontWeight: 500 }}>{props.label ? props.label : "已选附件"}</div>
          <List items={selectedFiles} onRenderCell={renderFileItem} />
        </div>
      )}

      {/* 错误提示 */}
      {props.error && <div style={{ color: '#a80000', fontSize: 12, marginTop: 8 }}>{props.error}</div>}
    </div>
  );
};

AttachmentInput.propTypes = {
  props: PropTypes.shape({
    class: PropTypes.string,
    error: PropTypes.string,
    label: PropTypes.string,
    buttonText: PropTypes.string,
    multiple: PropTypes.bool, // 是否允许选择多个文件
    accept: PropTypes.string, // 限制文件类型,如".pdf,.docx"
    disabled: PropTypes.bool,
    allowDuplicate: PropTypes.bool, // 是否允许重复添加相同文件
    required: PropTypes.bool,
  }),
  otherProps: PropTypes.object,
  onFilesChange: PropTypes.func.isRequired, // 返回选中的文件数组
};

// 默认属性
AttachmentInput.defaultProps = {
  props: {
    multiple: true,
    allowDuplicate: false,
    disabled: false,
    required: false,
  },
};

export default AttachmentInput;

关键功能说明

  • UI一致性:使用Office UI Fabric组件构建,和你的TextFieldInput风格统一,避免样式冲突。
  • 核心能力:支持单/多文件选择、文件类型限制、文件预览(名称/大小)、单个文件移除。
  • SPFx适配:依赖@microsoft/sp-core-library的getFileSizeString方法格式化文件大小,符合SPFx生态。
  • 回调机制:通过onFilesChange返回选中的File数组,方便后续实现上传到SharePoint的逻辑。

组件使用示例

import AttachmentInput from './AttachmentInput';

// 在你的Web部件或父组件中使用
const YourComponent = () => {
  const [files, setFiles] = React.useState<File[]>([]);
  const [isSubmitted, setIsSubmitted] = React.useState(false);

  const handleFilesChange = (selectedFiles: File[]) => {
    setFiles(selectedFiles);
    // 这里可以添加上传到SharePoint的逻辑
  };

  const handleSubmit = () => {
    setIsSubmitted(true);
    if (files.length === 0) return;
    // 提交逻辑
  };

  return (
    <div>
      <AttachmentInput
        props={{
          label: "文档附件",
          buttonText: "上传文档",
          accept: ".pdf,.docx,.xlsx",
          required: true,
          error: files.length === 0 && isSubmitted ? "请至少添加一个附件" : ""
        }}
        onFilesChange={handleFilesChange}
      />
      <Button onClick={handleSubmit} text="提交" style={{ marginTop: 16 }} />
    </div>
  );
};

后续上传逻辑提示

如果需要将文件上传到SharePoint文档库,可以在handleFilesChange中调用SPFx的SPHttpClient:

import { SPHttpClient } from '@microsoft/sp-http';

// 示例:上传单个文件到指定文档库
const uploadFileToSP = async (file: File, context: any, libraryName: string) => {
  const fileName = file.name;
  const fileUrl = `${context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${libraryName}')/rootfolder/files/add(overwrite=true,url='${fileName}')`;

  const response = await context.spHttpClient.post(
    fileUrl,
    SPHttpClient.configurations.v1,
    {
      body: file,
      headers: {
        "Accept": "application/json;odata=nometadata",
        "Content-Type": file.type
      }
    }
  );

  return response.json();
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 09:20:23