npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sl-material/sl-import

v1.2.0-beta1

Published

导入组件 - 支持批量导入文件,提供多种模板配置

Downloads

43

Readme

@sl-material/sl-import

批量导入弹窗组件,支持多种导入模板配置、文件分片上传、品牌选择、图片比例选择、AI 智能导入等功能。

特性

  • 多种模板类型 - 内置 5 种预设模板,支持自定义配置
  • AI 智能导入 - 可选 AI 智能解析模块,支持任意格式文件与字段自动映射
  • 文件分片上传 - 支持大文件分片上传,自动合并,支持延迟上传
  • 多文件上传 - 支持批量文件上传,横向滚动列表展示
  • 上传时机控制 - 支持立即上传和延迟上传两种模式
  • 灵活文件校验 - 可选的文件数量和大小校验,不传则不校验
  • 品牌选择 - 支持静态数据或异步加载品牌列表
  • 图片比例选择 - 支持配置图片裁剪比例选项
  • 日期范围选择 - 支持月份和日期范围选择
  • 统一 Loading 样式 - 文件卡片和全局遮罩使用相同的渐变圆环动画
  • 纯原生实现 - 不依赖 Vue/React 等框架,可在任何环境使用
  • TypeScript 支持 - 完整的类型定义
  • 标准化国际化 - 支持 zh_CN、en_US、ja_JP、zh_HK、zh_TW、id 等语言

安装

npm install @sl-material/sl-import
# 或
yarn add @sl-material/sl-import
# 或
pnpm add @sl-material/sl-import

兼容性

  • 浏览器支持:Chrome 80+, Firefox 72+, Safari 13.1+, Edge 80+
  • Node.js 支持:14.0+
  • TypeScript 支持:4.0+
  • ES 版本:编译为 ES2020,兼容现代浏览器和 Node.js 环境

使用方式

ES 模块

import { ImportDialog } from "@sl-material/sl-import";

CommonJS

const { ImportDialog } = require("@sl-material/sl-import");

注意:CSS 样式已自动内联到 JS 文件中,无需手动导入 CSS 文件。

API 文档

ImportDialog 类

静态方法

| 方法 | 参数 | 返回值 | 说明 | | --------------------- | ------------------ | --------------------------- | ---------------------- | | open(options) | OpenModalOptions | Promise<ImportSubmitData> | 打开弹窗,返回 Promise | | close() | - | void | 关闭弹窗 | | setLoading(loading) | boolean | void | 设置加载状态 | | isReady() | - | boolean | 检查服务是否就绪 | | setLocale(locale) | I18nLocale | void | 设置国际化语言 | | getLocale() | - | I18nLocale | 获取当前语言 |

实例方法

| 方法 | 参数 | 返回值 | 说明 | | ---------------------------- | ------------------ | ------ | -------------------- | | openModal(options) | OpenModalOptions | void | 打开导入弹窗 | | closeModal() | - | void | 关闭弹窗 | | setConfirmLoading(loading) | boolean | void | 设置确认按钮加载状态 | | hide() | - | void | 隐藏弹窗 | | destroy() | - | void | 销毁实例 |

OpenModalOptions 配置

| 参数 | 类型 | 默认值 | 说明 | | -------------------- | ------------------------------------------------- | ------------ | ------------------------------------------------------------ | | type | ExportTypeEnum | excel | 导出文件类型 | | title | string | '批量导入' | 弹窗标题 | | width | string | '480px' | 弹窗宽度 | | templateType | TemplateTypeEnum | BASE | 模板类型 | | tabs | TabConfig[] | [] | 自定义标签页配置 | | defaultActiveTab | number | 0 | 默认激活的标签页 | | brandData | BrandOption[] \| (() => Promise<BrandOption[]>) | - | 品牌数据或异步获取函数 | | brandPlaceholder | string | '默认品牌' | 品牌选择占位符 | | imageRatios | ImageRatioOption[] | - | 图片比例选项 | | extendImageRatios | ImageRatioOption[] | - | 扩展图片比例选项 | | tips | string[] | - | 提示信息 | | templateUrl | string | - | 模板下载链接 | | uploadTitle | string | - | 上传区域标题 | | uploadLinkText | string | - | 上传链接文字 | | uploadHint | string | - | 上传提示信息 | | acceptTypes | string | - | 接受的文件类型 | | showBrand | boolean | - | 是否显示品牌选择 | | showImageRatio | boolean | - | 是否显示图片比例选择 | | showMonth | boolean | - | 是否显示月份选择 | | showDateRange | boolean | - | 是否显示日期范围选择 | | isAiImport | boolean | false | 是否显示 AI 智能解析模块;为 true 时默认开启 AI 导入开关 | | dateRangeShortcuts | DateRangeShortcut[] | - | 日期快捷选项 | | disabledDate | (date: Date) => boolean | - | 禁用日期函数 | | locale | I18nLocale | - | 国际化语言 | | uploadConfig | UploadConfig | - | 上传相关配置,见下方 |

上传配置 (UploadConfig)

所有上传相关配置统一放在 uploadConfig 对象中:

| 参数 | 类型 | 默认值 | 说明 | | ----------------- | ----------------------------------------------- | ------- | ---------------------------- | | autoUpload | boolean | false | 是否立即上传文件 | | multiple | boolean | false | 是否支持多文件上传 | | maxFiles | number | - | 最大文件数量,不传则不校验 | | maxFileSize | number | - | 单文件最大大小,不传则不校验 | | maxFileSizeUnit | 'KB' \| 'MB' \| 'GB' | 'MB' | 文件大小单位 | | customUpload | (file, context?, onProgress?) => Promise<any> | - | 自定义上传 / 预处理函数 | | chunkedUpload | ChunkedUploadConfig | - | 分片上传策略配置 | | confirmLoading | boolean | true | 确定按钮 loading 控制 | | onFileChange | (files: FileUploadItem[]) => void | - | 文件列表变化回调 | | onProgress | (file, progress) => void | - | 上传进度回调 | | onSuccess | (file, response) => void | - | 上传成功回调 | | onError | (file, error) => void | - | 上传失败回调 |

文件校验说明:

  • maxFiles:限制最大文件数量,不传递则不进行数量校验
  • maxFileSize 和 maxFileSizeUnit 组合使用限制文件大小,不传递则不进行大小校验
  • 例如:maxFileSize: 10, maxFileSizeUnit: 'KB' 表示最大 10KB
分片上传策略 (ChunkedUploadConfig)
interface ChunkedUploadConfig {
  initUpload: (
    fileName: string,
    fileSize: number,
  ) => Promise<ChunkUploadInitResult>;
  getResumableConfig: (initResult: ChunkUploadInitResult) => ResumableConfig;
  mergeChunks: (uploadSessionId: string) => Promise<any>;
}
文件操作上下文 (CustomUploadContext)

customUpload 的第二个参数:

interface CustomUploadContext {
  /** 当前文件列表 */
  fileList: FileUploadItem[];
  /** 删除指定索引的文件 */
  removeFile: (index: number) => void;
  /** 当前是否启用 AI 导入 */
  isAiImport?: boolean;
}

回调函数

| 参数 | 类型 | 说明 | | -------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | onBeforeConfirm | (data) => void \| Promise<void> | 确认前回调 | | onConfirm | (data) => void \| ImportConfirmResult \| Promise<void \| ImportConfirmResult> | 确认回调;返回 { success: true } 时自动关闭;不返回值时可由业务侧手动关闭 | | onBeforeCancel | () => void \| Promise<void> | 取消前回调 | | onCancel | () => void | 取消回调 | | onTabChange | (data) => void | 标签切换回调 | | onDownloadTemplate | (data: { url: string; activeTab?: TabConfig }) => void | 下载模板回调 | | onFileChange | (files) => void | 文件变化回调(也可写在 uploadConfig 中) | | onUploadProgress | (file, progress) => void | 上传进度回调 | | onUploadSuccess | (file, response) => void | 上传成功回调 | | onUploadError | (file, error) => void | 上传失败回调 |

模板类型 (TemplateTypeEnum)

| 类型 | 说明 | | ----------- | ------------------------ | | BASE | 基础模板,仅文件上传 | | TEMPLATE1 | 品牌 + 图片比例选择 | | TEMPLATE2 | 覆盖/增量导入 + 图片上传 | | TEMPLATE3 | 月份选择 + 营业目标导入 | | TEMPLATE4 | 品牌选择 + 菜谱方案导入 | | TEMPLATE5 | 合批修改价格 + 日期范围 |

国际化 (I18nLocaleEnum)

| 语言 | 枚举值 | | ---------------- | ------- | | 简体中文 | zh_CN | | 繁體中文(香港) | zh_HK | | 繁體中文(臺灣) | zh_TW | | 英语(美国) | en_US | | 日本语 | ja_JP | | 印尼语 | id |

类型定义

interface TabConfig {
  label: string;
  key: string;
}

interface BrandOption {
  label: string;
  value: string | number;
}

interface ImageRatioOption {
  label: string;
  value: string;
}

interface ImportFormData {
  brandId: string | number | undefined;
  imageRatio: string;
  month: number;
  dateRange: [Date | null, Date | null];
  /** 是否启用 AI 导入(用户可在弹窗内开关切换) */
  isAiImport: boolean;
}

interface ImportSubmitData {
  files: File[];
  formData: ImportFormData;
  activeTab: TabConfig | undefined;
  activeTabIndex: number;
  /** 当前是否启用 AI 导入 */
  isAiImport?: boolean;
}

interface ImportConfirmResult {
  success?: boolean;
  message?: string;
  [key: string]: any;
}

interface FileUploadItem {
  id: string;
  file: File;
  name: string;
  size: number;
  progress: number;
  status: "pending" | "uploading" | "success" | "error";
  errorMessage?: string;
  uploadSessionId?: string;
  response?: any;
}

AI 导入说明

开启 isAiImport: true 后:

  1. 弹窗内展示「AI 智能解析」卡片,用户可通过开关切换标准导入 / AI 导入
  2. 开启 AI 时,上传区域使用 AI 样式,确定按钮文案变为「AI 导入」
  3. 确认提交时,可通过 data.formData.isAiImport 或 data.isAiImport 获取当前开关状态
  4. customUpload 的 context.isAiImport 也会同步当前开关状态,便于按模式走不同上传逻辑

使用示例

基础导入(立即上传)

import { ImportDialog, TemplateTypeEnum } from "@sl-material/sl-import";

ImportDialog.open({
  title: "导入数据",
  templateType: TemplateTypeEnum.BASE,
  templateUrl: "/template/data_template.xlsx",
  tips: ["提示信息 1", "提示信息 2"],
  uploadConfig: {
    autoUpload: true, // 选择文件后立即上传
    customUpload: async (file, onProgress) => {
      const formData = new FormData();
      formData.append("file", file);

      const response = await fetch("/api/upload", {
        method: "POST",
        body: formData,
      });

      return response.json();
    },
  },
});

AI 智能导入

设置 isAiImport: true 显示 AI 智能解析模块。用户可在弹窗内切换开关;确认时根据 formData.isAiImport 走不同业务逻辑。

import {
  ImportDialog,
  TemplateTypeEnum,
  ExportTypeEnum,
  I18nLocaleEnum,
} from "@sl-material/sl-import";

ImportDialog.open({
  locale: I18nLocaleEnum.ZH_CN,
  type: ExportTypeEnum.excel,
  title: "AI 智能导入",
  templateType: TemplateTypeEnum.BASE,
  isAiImport: true,
  uploadTitle: "上传文件",
  uploadLinkText: "点击上传",
  uploadHint: "文件仅支持扩展名:.rar .zip .doc .docx .pdf...",
  acceptTypes: ".rar,.zip,.doc,.docx,.pdf,.xls,.xlsx,.csv",
  tips: [
    "开启 AI 智能解析后,系统会自动识别文件字段并建立映射",
    "支持非标准模板文件导入,确认时可通过 formData.isAiImport 获取开关状态",
  ],
  uploadConfig: {
    confirmLoading: false,
    customUpload: async (file, context) => {
      // context.isAiImport 反映当前开关状态
      console.log("是否 AI 导入:", context?.isAiImport);
      return { file, aiImport: context?.isAiImport };
    },
  },
  onConfirm: async (data) => {
    if (data.formData.isAiImport) {
      // AI 导入:走智能解析接口
      await submitAiImport(data.files);
    } else {
      // 标准导入:走模板校验接口
      await submitStandardImport(data.files);
    }
    return { success: true };
  },
});

使用文件操作功能

import { ImportDialog, TemplateTypeEnum } from "@sl-material/sl-import";

ImportDialog.open({
  title: "导入数据",
  templateType: TemplateTypeEnum.BASE,
  uploadConfig: {
    autoUpload: false,
    customUpload: async (file, context) => {
      console.info("file", file, "context", context);
      return { file };
    },
  },
  onConfirm: async (data) => {
    // 此时文件已上传完成,data.files 包含上传结果
    console.log("上传结果:", data);
    return { success: true };
  },
});

多文件上传

ImportDialog.open({
  locale: I18nLocaleEnum.ZH_CN,
  type: ExportTypeEnum.excel,
  templateType: TemplateTypeEnum.BASE,
  templateUrl: "/template/base_template.xlsx",
  tips: ["提示信息 1", "提示信息 2"],
  onConfirm: async (data) => {
    return submitImportMock(data);
  },
  uploadConfig: {
    multiple: true,
    confirmLoading: false,
    maxFileSize: 1000,
    maxFileSizeUnit: "KB",
    customUpload: async (file: File, context) => {
      if (file.name.includes(",")) {
        context?.removeFile(
          context?.fileList.findIndex((f) => f.name === file.name),
        );
      }
      // 返回值会存储在 fileItem.response 中
      return { file };
    },
  },
});

文件校验配置

不传 maxFiles / maxFileSize 则不校验:

ImportDialog.open({
  title: "导入数据",
  templateType: TemplateTypeEnum.BASE,
  uploadConfig: {
    multiple: true,
    customUpload: async (file, context, onProgress) => {
      return { file };
    },
  },
  onConfirm: async (data) => {
    console.log("上传结果:", data);
    return { success: true };
  },
});

限制数量与大小:

ImportDialog.open({
  title: "导入数据",
  templateType: TemplateTypeEnum.BASE,
  uploadConfig: {
    multiple: true,
    maxFiles: 10,
    maxFileSize: 50,
    maxFileSizeUnit: "MB",
    customUpload: async (file, context, onProgress) => {
      return { file };
    },
  },
  onConfirm: async (data) => {
    console.log("上传结果:", data);
    return { success: true };
  },
});

禁用确定按钮 Loading

ImportDialog.open({
  title: "导入数据",
  templateType: TemplateTypeEnum.BASE,
  uploadConfig: {
    autoUpload: false,
    confirmLoading: false,
    customUpload: async (file, onProgress) => {
      // 上传逻辑
    },
  },
  onConfirm: async (data) => {
    console.log("上传结果:", data.files);
    return { success: true };
  },
});

分片上传(立即上传)

注意:分片上传目前只支持单个文件上传。

ImportDialog.open({
  title: "大文件上传",
  templateType: TemplateTypeEnum.BASE,
  uploadConfig: {
    autoUpload: true,
    maxFileSize: 500,
    maxFileSizeUnit: "MB",
    chunkedUpload: {
      initUpload: async (fileName, fileSize) => {
        const response = await fetch("/api/upload/init", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ fileName, fileSize }),
        });
        return response.json();
      },
      getResumableConfig: (initResult) => ({
        target: "/api/upload/chunk",
        headers: {
          Authorization: `Bearer ${token}`,
        },
        query: { uploadSessionId: initResult.uploadSessionId },
      }),
      mergeChunks: async (uploadSessionId) => {
        const response = await fetch(`/api/upload/merge/${uploadSessionId}`, {
          method: "POST",
        });
        return response.json();
      },
    },
  },
});

分片上传(点击确定后上传)

ImportDialog.open({
  type: ExportTypeEnum.excel,
  title: "分片上传(延迟)",
  templateType: TemplateTypeEnum.BASE,
  tabs: [{ label: "分片延迟上传", key: "chunked-delayed" }],
  tips: [
    "1. 选择文件后不会立即上传",
    "2. 点击确定按钮时才开始分片上传",
    "3. autoUpload: false(默认值)",
  ],
  uploadTitle: "选择大文件",
  uploadHint: "选择文件后点击确定开始分片上传",
  acceptTypes: ".xlsx,.xls,.csv,.zip",
  uploadConfig: {
    maxFileSize: 500,
    maxFileSizeUnit: "MB",
    autoUpload: false,
    chunkedUpload: {
      initUpload: async (fileName: string, fileSize: number) => {
        const response = await initImportTask({ fileName, fileSize });
        return {
          uploadSessionId: response?.uploadSessionId,
          partSize: response?.partSize,
          totalParts: response?.totalParts,
        };
      },
      getResumableConfig: (initResult) => ({
        target: `/api/file/upload/part?uploadSessionId=${initResult.uploadSessionId}`,
        headers: {
          Authorization: `Bearer ${token}`,
        },
      }),
      mergeChunks: async (uploadSessionId: string) => {
        return uploadFileMerge(uploadSessionId);
      },
    },
  },
  onConfirm: async (data) => {
    console.info("openChunkedUploadDelayed>>>", data);
    return { success: true, message: "导入成功" };
  },
});

带品牌选择的导入

ImportDialog.open({
  title: "导入商品",
  templateType: TemplateTypeEnum.TEMPLATE1,
  brandData: async () => {
    const res = await fetch("/api/brands");
    return res.json();
  },
  uploadConfig: {
    autoUpload: false,
  },
  onConfirm: async (data) => {
    console.log("品牌 ID:", data.formData.brandId);
    console.log("图片比例:", data.formData.imageRatio);
    console.log("上传文件:", data.files);

    await processImport(data.files, {
      brandId: data.formData.brandId,
      imageRatio: data.formData.imageRatio,
    });

    return { success: true };
  },
});

自定义日期范围

ImportDialog.open({
  title: "导入报表",
  templateType: TemplateTypeEnum.TEMPLATE5,
  showDateRange: true,
  dateRangeShortcuts: [
    {
      label: "最近7天",
      value: [new Date(Date.now() - 6 * 24 * 60 * 60 * 1000), new Date()],
    },
    {
      label: "最近30天",
      value: [new Date(Date.now() - 29 * 24 * 60 * 60 * 1000), new Date()],
    },
  ],
  disabledDate: (date) => {
    return date > new Date();
  },
});

手动控制关闭弹窗

onConfirm 不返回 { success: true } 时,需业务侧自行调用 ImportDialog.close():

ImportDialog.open({
  onConfirm: async (data) => {
    await submitImport(data);
    ImportDialog.close();
    // 继续执行下一步业务逻辑
  },
});

国际化

import { ImportDialog, I18nLocaleEnum } from "@sl-material/sl-import";

ImportDialog.open({
  locale: I18nLocaleEnum.ZH_CN,
  // locale: I18nLocaleEnum.EN_US,
  // locale: I18nLocaleEnum.JA_JP,
  // ... 其他配置
});

UMD 使用方式

<script src="https://unpkg.com/@sl-material/sl-import/dist/sl-import.umd.umd.js"></script>
<script>
  const { ImportDialog } = window.SlImport;

  ImportDialog.open({
    title: "批量导入",
    templateType: "BASE",
  });
</script>

注意:UMD 版本同样已内联 CSS,无需额外引入样式文件。

更新日志

v1.1.0-beta4

新增特性

  • AI 智能导入:isAiImport 控制是否展示 AI 智能解析模块,支持开关切换与确认态回传
  • 灵活文件校验:maxFiles 和 maxFileSize 参数改为可选,不传则不校验
  • 标准化国际化:统一语言代码格式为 zh_CN、en_US、ja_JP 等
  • 新增语言支持:添加 zh_HK(繁體中文-香港)支持
  • 统一 Loading 样式:文件卡片和全局遮罩使用相同的渐变圆环动画
  • 分片上传延迟模式:支持文件选择后延迟上传,用户可手动控制上传时机

修复问题

  • 修复 loading 动画跳动问题
  • 修复分片上传延迟模式下的配置问题
  • 优化文件校验逻辑的时机和顺序

优化改进

  • 所有硬编码中文提示改为国际化调用
  • 优化文件大小单位配置,支持 KB、MB、GB
  • 改进错误提示信息的准确性和友好性

License

MIT