@sl-material/sl-import
v1.2.0-beta1
Published
导入组件 - 支持批量导入文件,提供多种模板配置
Downloads
43
Maintainers
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 后:
- 弹窗内展示「AI 智能解析」卡片,用户可通过开关切换标准导入 / AI 导入
- 开启 AI 时,上传区域使用 AI 样式,确定按钮文案变为「AI 导入」
- 确认提交时,可通过
data.formData.isAiImport或data.isAiImport获取当前开关状态 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
