mcpField
E
experience-ui-bundle-file-upload-generate

experience-ui-bundle-file-upload-generate

github.com/forcedotcom开发1.1k5.2k 次安装1 天前更新
代码仓库

关于 experience-ui-bundle-file-upload-generate

面向 Salesforce React UI bundle 的文件上传工作流技能,提供上传 API 和进度回调;界面由开发者自行构建。

主要功能

  1. 01通过回调跟踪上传进度
  2. 02将文件关联到 Salesforce 记录
  3. 03延后关联记录
  4. 04使用 AbortController 取消上传
  5. 05获取当前用户 ID

适用场景

  1. 01为现有记录附加文件
  2. 02创建记录前先上传文件
  3. 03构建自定义上传与进度界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill experience-ui-bundle-file-upload-generate

安装后在 Claude Code 中输入/experience-ui-bundle-file-upload-generate确认已加载。

详细介绍

文件上传 API(工作流)

当用户希望在 React UI 包中实现文件上传功能时,请遵循此工作流。此功能仅提供 API——你必须使用提供的 API 自行构建 UI 组件。

重要:这是一个仅提供 API 的包

此包导出的是程序化 API,而非 React 组件或钩子。你将:

  • 使用 upload() 函数处理文件上传并跟踪进度
  • 构建自己的自定义 UI(文件输入框、拖放区、进度条等)
  • 通过 onProgress 回调跟踪上传进度

请勿:

  • 期待有预构建组件,例如 <FileUpload />——此包不会导出这些组件
  • 尝试导入 React 钩子,例如 useFileUpload——此包不会导出这些钩子
  • 查找拖放区组件——此包不会导出这些组件

源代码中包含用于演示的参考组件,但它们无法通过导入使用。请将其用作示例,自行构建 UI。

1. 安装此包

npm install @salesforce/ui-bundle-template-feature-react-file-upload

依赖项会自动安装:

  • @salesforce/ui-bundle(API 客户端)
  • @salesforce/platform-sdk(数据 SDK;旧名称 @salesforce/sdk-data 已停用——请参阅 experience-ui-bundle-salesforce-data-access 技能)

2. 了解三种上传模式

模式 A:基本上传(不关联记录)

将文件上传到 Salesforce,并获取每个文件对应的 contentBodyId。不会创建 ContentVersion 记录。

适用场景:

  • 用户希望先上传文件,之后再创建记录或将文件关联到记录
  • 构建多步骤表单,但记录尚不存在
  • 延迟关联记录的场景
import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";

const results = await upload({
  files: [file1, file2],
  onProgress: (progress) => {
    console.log(`${progress.fileName}: ${progress.status} - ${progress.progress}%`);
  },
});

// results[0].contentBodyId: "069..." (always available)
// results[0].contentVersionId: undefined (no record linked)
模式 B:上传并立即关联记录

上传文件,并通过创建 ContentVersion 记录立即将其关联到现有 Salesforce 记录。

适用场景:

  • 记录已存在(Account、Opportunity、Case 等)
  • 用户希望文件立即附加到记录
  • 直接上传并附加文件的场景
import { upload } from "@salesforce/ui-bundle-template-feature-react-file-upload";

const results = await upload({
  files: [file1, file2],
  recordId: "001xx000000yyyy", // Existing record ID
  onProgress: (progress) => {
    console.log(`${progress.fileName}: ${progress.status} - ${progress.progress}%`);
  },
});

// results[0].contentBodyId: "069..." (always available)
// results[0].contentVersionId: "068..." (linked to record)
模式 C:延迟关联记录(记录创建流程)

先在没有关联记录的情况下上传文件,然后在记录创建后再将文件关联到记录。

适用场景:

  • 构建“创建带附件的记录”表单
  • 记录要到表单提交后才会创建
  • 需要先上传文件,之后才能知道最终的记录 ID
import {
  upload,
  createContentVersion,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";

// Step 1: Upload files (no recordId)
const uploadResults = await upload({
  files: [file1, file2],
  onProgress: (progress) => console.log(progress),
});

// Step 2: Create the record
const newRecordId = await createRecord(formData);

// Step 3: Link uploaded files to the new record
for (const file of uploadResults) {
  const contentVersionId = await createContentVersion(
    new File([""], file.fileName),
    file.contentBodyId,
    newRecordId,
  );
}

3. 构建自定义 UI

此包提供后端功能,前端由你构建。以下是一个最简示例:

import {
  upload,
  type FileUploadProgress,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";
import { useState } from "react";

function CustomFileUpload({ recordId }: { recordId?: string }) {
  const [progress, setProgress] = useState<Map<string, FileUploadProgress>>(new Map());

  const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(event.target.files || []);

    await upload({
      files,
      recordId,
      onProgress: (fileProgress) => {
        setProgress((prev) => new Map(prev).set(fileProgress.fileName, fileProgress));
      },
    });
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileSelect} />

      {Array.from(progress.entries()).map(([fileName, fileProgress]) => (
        <div key={fileName}>
          {fileName}: {fileProgress.status} - {fileProgress.progress}%
          {fileProgress.error && <span>Error: {fileProgress.error}</span>}
        </div>
      ))}
    </div>
  );
}

4. 跟踪上传进度

每个文件在上传过程中经过不同阶段时,onProgress 回调都会多次触发:

状态时机进度值
"pending"文件已加入上传队列0
"uploading"正在上传(XHR)0-100(百分比)
"processing"正在创建 ContentVersion(如果提供了 recordId)0
"success"上传完成100
"error"上传失败0

务必提供可视化反馈:

  • 显示文件名
  • 显示当前状态
  • 在 "uploading" 状态下显示进度条
  • 在 "error" 状态下显示错误消息

5. 取消上传(可选)

使用 AbortController 允许用户取消上传:

const abortController = new AbortController();

const handleUpload = async (files: File[]) => {
  try {
    await upload({
      files,
      signal: abortController.signal,
      onProgress: (progress) => console.log(progress),
    });
  } catch (error) {
    console.error("Upload cancelled or failed:", error);
  }
};

const cancelUpload = () => {
  abortController.abort();
};

6. 关联到当前用户(特殊情况)

如果用户希望将文件上传到自己的个人资料或个人库:

import {
  upload,
  getCurrentUserId,
} from "@salesforce/ui-bundle-template-feature-react-file-upload";

const userId = await getCurrentUserId();
await upload({ files, recordId: userId });

API 参考

upload(options)

主要上传 API,负责处理完整流程并跟踪进度。

interface UploadOptions {
  files: File[];
  recordId?: string | null; // If provided, creates ContentVersion
  onProgress?: (progress: FileUploadProgress) => void;
  signal?: AbortSignal; // Optional cancellation
}

interface FileUploadProgress {
  fileName: string;
  status: "pending" | "uploading" | "processing" | "success" | "error";
  progress: number; // 0-100 for uploading, 0 for other states
  error?: string;
}

interface FileUploadResult {
  fileName: string;
  size: number;
  contentBodyId: string; // Always available
  contentVersionId?: string; // Only if recordId was provided
}

返回值: Promise<FileUploadResult[]>

createContentVersion(file, contentBodyId, recordId)

根据之前上传的文件,手动创建 ContentVersion 记录。

async function createContentVersion(
  file: File,
  contentBodyId: string,
  recordId: string,
): Promise<string | undefined>;

参数:

  • file — File 对象(用于获取名称等元数据)
  • contentBodyId — 上一次上传返回的 ContentBody ID
  • recordId — 用于 FirstPublishLocationId 的记录 ID

返回: 成功时返回 ContentVersion ID

getCurrentUserId()

获取当前用户的 Salesforce ID。

async function getCurrentUserId(): Promise<string>;

返回: 当前用户 ID

常见 UI 模式

带按钮的文件输入框
<input type="file" multiple accept=".pdf,.doc,.docx,.jpg,.png" onChange={handleFileSelect} />
拖放区域

使用原生事件构建自己的拖放区域:

function DropZone({ onDrop }: { onDrop: (files: File[]) => void }) {
  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    const files = Array.from(e.dataTransfer.files);
    onDrop(files);
  };

  return (
    <div
      onDrop={handleDrop}
      onDragOver={(e) => e.preventDefault()}
      style={{ border: "2px dashed #ccc", padding: "2rem" }}
    >
      Drop files here
    </div>
  );
}
进度条
{
  progress.status === "uploading" && (
    <div style={{ width: "100%", background: "#eee" }}>
      <div
        style={{
          width: `${progress.progress}%`,
          background: "#0176d3",
          height: "8px",
        }}
      />
    </div>
  );
}

Agent 决策树

用户要求实现文件上传功能:

  1. 询问记录上下文:

    • “您想将上传的文件关联到特定记录,还是先上传文件再关联?”
  2. 根据回答:

    • 关联到现有记录 → 使用模式 B,并提供 recordId
    • 先上传,之后再关联 → 使用模式 A(不提供 recordId),然后使用模式 C 进行关联
    • 关联到当前用户 → 使用模式 B,并调用 getCurrentUserId()
  3. 构建 UI:

    • 创建文件输入框或拖放区域(此包未提供)
    • 为每个文件显示进度(状态 + 进度条)
    • 在 UI 中处理错误
  4. 测试实现:

    • 确认进度回调能正确触发
    • 检查是否返回了 contentBodyId
    • 如果提供了 recordId,确认是否返回了 contentVersionId

参考实现

此包在 src/features/fileupload/ 中提供了一个参考实现,其中包括:

  • FileUpload.tsx — 带有拖放区域和对话框的完整组件
  • FileUploadDialog.tsx — 进度跟踪对话框
  • FileUploadDropZone.tsx — 拖放区域
  • useFileUpload.ts — 用于状态管理的 React hook

这些内容不会导出,但可以作为示例查看。阅读源文件,了解构建自有 UI 的模式。

故障排除

上传因 CORS 错误而失败:

  • 确保 UI bundle 已正确部署到 Salesforce,或正在 localhost 上运行
  • 检查组织是否已在 CORS 设置中允许该来源

没有进度更新:

  • 确认已提供 onProgress 回调
  • 检查回调函数是否正确更新 React 状态

未创建 ContentVersion:

  • 确认已向 upload() 函数提供 recordId
  • 检查记录 ID 是否有效且存在于组织中
  • 确保用户拥有创建 ContentVersion 记录的权限

文件已上传,但未显示在记录中:

  • 确认 recordId 正确
  • 检查是否已创建 ContentVersion(在结果中查找 contentVersionId)
  • 确认用户有权查看该记录中的文件

请勿执行以下操作

  • 从头编写 XHR/fetch 上传逻辑——请使用 upload() API
  • 尝试导入 <FileUpload /> 组件——它不会导出
  • 尝试导入 useFileUpload hook——它不会导出
  • 此功能可用时,不要使用第三方文件上传库
  • 不要跳过进度跟踪——务必向用户提供反馈
  • 不要忽略错误——务必处理并显示错误消息

评论

支持 Markdown