@sl-material/sl-import
v1.1.1
Published
导入组件 - 支持批量导入文件,提供多种模板配置
Maintainers
Readme
@sl-material/sl-import
批量导入弹窗组件,支持多种导入模板配置、文件分片上传、品牌选择、图片比例选择等功能。
特性
- 多种模板类型 - 内置 5 种预设模板,支持自定义配置
- 文件分片上传 - 支持大文件分片上传,自动合并,支持延迟上传
- 多文件上传 - 支持批量文件上传,横向滚动列表展示
- 上传时机控制 - 支持立即上传和延迟上传两种模式
- 灵活文件校验 - 可选的文件数量和大小校验,不传则不校验
- 品牌选择 - 支持静态数据或异步加载品牌列表
- 图片比例选择 - 支持配置图片裁剪比例选项
- 日期范围选择 - 支持月份和日期范围选择
- 统一 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 文件。
使用示例
基础导入(立即上传)
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();
},
},
});使用文件操作功能
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: "zh_CN" as I18nLocaleEnum,
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,
// maxFiles: 2,
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 };
},
},
});文件校验配置
ImportDialog.open({
title: "导入数据",
templateType: TemplateTypeEnum.BASE,
uploadConfig: {
multiple: true,
// 不配置 maxFiles 和 maxFileSize,则不进行任何校验
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, // 限制最多 10 个文件
maxFileSize: 50, // 限制单个文件最大 50MB
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, // 不显示确定按钮 loading
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, // 可选:限制文件大小为 500MB
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 });
console.info("initResult>>>", response);
return {
uploadSessionId: response?.uploadSessionId,
partSize: response?.partSize,
totalParts: response?.totalParts,
};
},
getResumableConfig: (initResult) => ({
target: `/newProxy/basic-data-io/api/file/upload/part?uploadSessionId=${initResult.uploadSessionId}`,
headers: {
"TCSL-BP-TOKEN":
window.sessionStorage.getItem("token") || slyToken,
},
// query: { uploadSessionId: initResult.uploadSessionId },
}),
mergeChunks: async (uploadSessionId: string) => {
const response = await uploadFileMerge(uploadSessionId);
return response;
},
},
},
onConfirm: async (data) => {
console.info("openChunkedUploadDelayed>>>", data);
return { success: true, message: "导入成功" };
},
});
```
### 带品牌选择的导入
```typescript
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();
},
});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[] | - | 扩展图片比例选项 |
| customTips | string[] | - | 自定义提示信息 |
| templateUrl | string | - | 模板下载链接 |
| uploadTitle | string | - | 上传区域标题 |
| uploadLinkText | string | - | 上传链接文字 |
| uploadHint | string | - | 上传提示信息 |
| acceptTypes | string | - | 接受的文件类型 |
| showBrand | boolean | - | 是否显示品牌选择 |
| showImageRatio | boolean | - | 是否显示图片比例选择 |
| showMonth | boolean | - | 是否显示月份选择 |
| showDateRange | boolean | - | 是否显示日期范围选择 |
| dateRangeShortcuts | DateRangeShortcut[] | - | 日期快捷选项 |
| disabledDate | (date: Date) => boolean | - | 禁用日期函数 |
| locale | I18nLocale | - | 国际化语言 |
上传配置 (UploadConfig)
所有上传相关配置统一放在 uploadConfig 对象中:
| 参数 | 类型 | 默认值 | 说明 |
| ----------------- | ----------------------------------------------- | ------- | ---------------------------- |
| autoUpload | boolean | false | 是否立即上传文件 |
| multiple | boolean | false | 是否支持多文件上传 |
| maxFiles | number | - | 最大文件数量,不传则不校验 |
| maxFileSize | number | - | 单文件最大大小,不传则不校验 |
| maxFileSizeUnit | 'KB' \| 'MB' \| 'GB' | 'MB' | 文件大小单位 |
| customUpload | (file, context?, onProgress?) => Promise<any> | - | 自定义上传函数 |
| chunkedUpload | ChunkUploadStrategy | - | 分片上传策略配置 |
| 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 - 例如:
maxFileSize: 500, maxFileSizeUnit: 'MB'表示最大 500MB - 校验参数完全可选,可根据需要灵活配置
分片上传策略 (ChunkUploadStrategy)
interface ChunkUploadStrategy {
initUpload: (params: InitUploadParams) => Promise<InitUploadResult>;
getResumableOptions: (initResult: InitUploadResult) => ResumableOptions;
mergeChunks: (uploadSessionId: string) => Promise<any>;
}文件操作上下文
customUpload 函数的第三个参数,提供基本的文件操作能力:
interface Context {
/** 当前文件列表 */
fileList: FileUploadItem[];
/** 删除指定索引的文件 */
removeFile: (index: number) => void;
}回调函数
| 参数 | 类型 | 说明 |
| ------------------ | --------------------------------- | ----------------------------------- |
| onBeforeConfirm | (data) => void \| Promise<void> | 确认前回调 |
| onConfirm | (data) => void | 确认回调,返回 {success: boolean} |
| onBeforeCancel | () => void \| Promise<void> | 取消前回调 |
| onCancel | () => void | 取消回调 |
| onTabChange | (data) => void | 标签切换回调 |
| onFileChange | (files) => void | 文件变化回调 |
| 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 |
使用方式:
import { ImportDialog, I18nLocaleEnum } from "@sl-material/sl-import";
ImportDialog.open({
locale: I18nLocaleEnum.ZH_CN, // 简体中文
// locale: I18nLocaleEnum.ZH_HK, // 繁體中文(香港)
// locale: I18nLocaleEnum.ZH_TW, // 繁體中文(臺灣)
// locale: I18nLocaleEnum.EN_US, // 英语(美国)
// locale: I18nLocaleEnum.JA_JP, // 日本语
// locale: I18nLocaleEnum.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];
}
interface ImportSubmitData {
files: File[];
formData: ImportFormData;
activeTab: TabConfig | undefined;
activeTabIndex: number;
}
interface FileUploadItem {
id: string;
file: File;
name: string;
size: number;
progress: number;
status: "pending" | "uploading" | "success" | "error";
errorMessage?: string;
uploadSessionId?: string;
response?: any;
}最新特性
🎯 灵活的文件校验
文件校验参数完全可选,可根据需要灵活配置:
// 不进行任何校验
uploadConfig: {
multiple: true,
customUpload: myUploadFunction
}
// 完整校验配置
uploadConfig: {
multiple: true,
maxFiles: 10, // 限制最多 10 个文件
maxFileSize: 50, // 限制单个文件最大 50MB
maxFileSizeUnit: "MB",
customUpload: myUploadFunction
}🌍 标准化国际化
支持 6 种语言,使用标准的语言代码格式:
zh_CN- 简体中文zh_HK- 繁體中文(香港)zh_TW- 繁體中文(臺灣)en_US- 英语(美国)ja_JP- 日本语id- 印尼语
⚡ 统一 Loading 动画
文件卡片和全局遮罩使用相同的渐变圆环动画,视觉效果统一且流畅。
🔄 分片上传延迟模式
支持分片上传的延迟模式,文件选择后不会立即上传,用户可以:
- 选择多个文件
- 预览文件列表
- 手动触发上传或删除文件
- 点击确认按钮开始上传
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
🚀 新增特性
- 灵活文件校验:
maxFiles和maxFileSize参数改为可选,不传则不校验 - 标准化国际化:统一语言代码格式为
zh_CN、en_US、ja_JP等 - 新增语言支持:添加
zh_HK(繁體中文-香港)支持 - 统一 Loading 样式:文件卡片和全局遮罩使用相同的渐变圆环动画
- 分片上传延迟模式:支持文件选择后延迟上传,用户可手动控制上传时机
🐛 修复问题
- 修复 loading 动画跳动问题
- 修复分片上传延迟模式下的配置问题
- 优化文件校验逻辑的时机和顺序
💎 优化改进
- 所有硬编码中文提示改为国际化调用
- 优化文件大小单位配置,支持 KB、MB、GB
- 改进错误提示信息的准确性和友好性
License
MIT
