高级 Excel 导入

高级 Excel 导入组件,提供导入时校验数据、支持导入主从数据、提供导入批次支持多人同时导入。和 Excel 导入组件不同,不能将数据导入到 UI,只能将数据导入到数据表。可以先导入到临时表,在导入成功事件中再调用一个服务,写入正式表。

特别说明

  • 导入图片时,图片存储的数据列类型必须是图片类型

导入服务

高级 Excel 导入组件,通过导入服务实现数据导入到数据表。这个导入服务无需开发者写代码,通过配置实现校验规则、主从数据导入等功能。

创建导入服务

导入服务是系统提供的特殊服务。在服务中新建请求时,“类型”选择“excel 高级/excel 导入”,即为创建导入服务,如下图所示

导入服务提供 excelFile 参数,如下图所示,接收前端上传的文件,开发者可以添加其他参数,在导入服务中使用

在表达式编辑器中显示出所有的请求参数,可直接用于表达式,如下图所示

特别说明 导入服务修改后,点“保存”按钮保存修改,点“重新编译”按钮使其立即生效

工作表配置

导入服务中提供工作表配置(工作表对应 Excel 文件中的 Sheet)和全局配置,在导入服务的“处理逻辑”中通过“设置”按钮,进入配置页面

配置页面如下图所示,默认只显示一个工作表的配置信息,通过右侧“+号”按钮添加新的工作表

工作表配置信息说明见下表

特别说明

  • 校验规则表达式不通过不属于错误,不触发错误回滚
  • 工作表中的列名与数据表中列名相同时,可以不添加工作表列
  • 在工作表列中定义的列,才能在表达式编辑器中显示,才能在表达式中使用
配置项 子项 说明
工作表名 字符串 全局配置中的工作表映射为按索引映射时,这个名称和 Excel 文件 Sheet 的名称无关;全局配置中的工作表映射为按名称映射时,这个名称对应 Excel 文件 Sheet 的名称;在导入主从数据时,在“从工作表”中会选择“主工作表”
类型 同构工作表、普通工作表 同构工作表:表示所有工作表使用相同定义,即 Excel 文件中每个 Sheet 的格式都相同时,只定义一个工作表即可;普通工作表:表示每个工作表需要单独定义。例如:导入主从数据时,主数据使用一个工作表,从数据使用另一个工作表,这两个工作表都是普通工作表
表头行数 整数 表头(包括列头)的总行数
列头行数 整数 列头的行数
数据表 数据集 选择一个要导入数据的数据集
导入模式 增量模式、覆盖模式 增量模式:表示不修改已存在值的列,只对空值列添加数据;覆盖模式:表示覆盖已有的全部数据
错误回滚 是、否 错误指的是 Java 代码执行出错,例如导入数据表时,因数据超长,导致数据库报错。这种情况下,开启回滚,整个工作表中的数据都不会导入;反之,跳过错误,导入不报错的数据
行校验规则 名称 字符串 规则的名称
行检验规则 规则表达式 表达式 设置导入校验规则,满足规则时导入,否则不导入
工作表列 列名 字符串 定义工作表中的列名。多列头时,使用 │ 分隔列名,例如:订单 │ 金额
工作表列 数据类型 自动、文本、数值 默认情况下使用自动,特殊情况下可设置,以设置的为准
工作表列 数据表列 数据列名 选择一个数据列
工作表列 校验表达式 表达式 设置当前列的校验规则
工作表列 计算表达式 表达式 设置当前列的计算规则
工作表列 主工作表 工作表 在从数据的工作表上,选择一个主数据的工作表
工作表列 主工作表查找列 工作表列 选择主工作表的一个工作表列,用两列值相等的方式,形成主从关联关系,实现主从导入

全局配置

全局配置信息说明见下表

配置项 说明
日期格式 yyyy-MM-dd 日期格式
日期时间格式 yyyy-MM-dd HH:mm:ss 日期时间格式
时间格式 HH:mm:ss 时间格式
忽略不匹配列 是、否 当工作表列名和数据集列名不同,且没有配置工作表列时,即为不匹配的列。忽略不匹配列,则不导入该列,不忽略则导入时报错
工作表映射 按索引映射、按名称映射 按索引映射时,工作表的顺序代表 Excel 文件中 Sheet 的顺序;按名称映射时,使用工作表名和 Sheet 名匹配

调用导入服务

在页面中,添加自定义的导入服务和高级 Excel 导入组件,在高级 Excel 组件的“导入服务”属性中选择自定义的导入服务,如下图所示

高级 Excel 导入组件提供“导入”操作,用于导入 Excel。

导入事件(48)

高级 Excel 导入组件提供导入事件,用于编程扩展。

  • 提供导入前事件,用于在系统执行导入前,修改 Excel 文件,再执行导入。例如判断导入的数据中某列的数据不满足要求就删除这行数据不做导入

在 main 服务中添加 excel 导入事件监听类,例如在 main/service/impl 目录下添加 ExcelImportListener.java,内容如下

package main.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;


import com.justep.util.event.ExcelImportEvent;
import com.justep.util.event.ExcelImportEvent.ExcelImportEventKind;

@Component
public class ExcelImportListener implements ApplicationListener<ExcelImportEvent> {


    public ExcelImportListener() {
        System.out.println("=== 创建 ExcelImportListener ===");
    }
    @Override
    public void onApplicationEvent(ExcelImportEvent event) {
        if (ExcelImportEventKind.Preprocess == event.getKind()) {
            File exceFile = event.getUploadExcelFile();
            if(exceFile.exists()) {
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(exceFile);
                    Workbook excel = WorkbookFactory.create(fis);
                     Sheet sheet = excel.getSheetAt(0);//获得sheet1表
                     for(int i=0;i<=sheet.getLastRowNum();i++){ //对行进行循环
                         Row row=sheet.getRow(i);
                         Cell cell=row.getCell(0);
                         //根据每行第一列的值判断是否导入这行数据  
                         if(cell.getStringCellValue().equals("test")){
                             //如果值是test就把对应这行删除不导入   
                             sheet.removeRow(row);
                         }
                     }
                     File tempFile = new File(exceFile.getPath());
                     FileOutputStream fileOut  = new FileOutputStream(tempFile);
                     excel.write(fileOut);
                     fileOut.close();
                     event.setOutputExcelFile(tempFile);

                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }    
}
  • 提供导入后事件,用于在系统导入后,获取导入数据,执行自定义导入。例如一行数据中包括主从数据,系统导入主数据,在导入后事件中写代码导入从数据

在 main 服务中添加 excel 导入事件监听类,例如在 main/service/impl 目录下添加 ExcelImportListener.java,内容如下

package main.service.impl;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.justep.util.event.ExcelImportEvent;
import com.justep.util.event.ExcelImportEvent.ExcelImportEventKind;

@Component
public class ExcelImportListener implements ApplicationListener<ExcelImportEvent> {

    @Autowired
    private FuwuCustomService fuwuCustomService;

    public ExcelImportListener() {
        System.out.println("=== 创建 ExcelImportListener ===");
    }
    @Override
    public void onApplicationEvent(ExcelImportEvent event) {
        if (ExcelImportEventKind.Preprocess == event.getKind()) {
            System.out.println("excel 导入前事件");
        } else if (ExcelImportEventKind.Postprocess == event.getKind()) {
            System.out.println("excel 导入后事件");
            System.out.println(JSON.toJSONString(event.getImported()));

            List<JSONObject> importArr = event.getImported();
            JSONObject importJson = importArr.get(0);
            String filePath = importJson.getString("file");

            try {
                Path path = Paths.get(filePath);
                List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
                String content = String.join(System.lineSeparator(), lines);
                System.out.println(content);

                if("importsalary".equals(event.getActionName())){
                    fuwuCustomService.importSalary(content);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Excel 导入事件类 ExcelImportEvent 的方法如下

方法名 用途
getActionName 获取动作名,即全小写的请求名
getKind 获取事件类型,用于判断是导入前事件,还是导入后事件
getUploadExcelFile 获取上传的 excel 文件
setOutputExcelFile 在导入前事件中,写入输出文件,用于系统执行 excel 导入
getImported 在导入后事件中,用于获取导入的数据

getImported 方法返回值格式如下,包括导入数据 JSON 文件、sheet 名和导入数据集名

[
    {
        "file": "/usr/local/x5/model-user/tmp/comp.excel/1AD0F5BAE0C04AB5BDC3D1184F3D55F8/service/main/sheet/sheet/main_gongzidrlsb.json",
        "sheetName": "Sheet1",
        "dataName": "/main/gongzidrlsb"
    }
]

Excel 文件内容如下图所示

1737108870163

导入数据 JSON 文件内容如下,包括 excel 文件中的数据和导入数据时产生的主键值,其中导入数据集中存在的列,key 改为列标识,不存在的列,保留 Excel 文件中的列名

{
    "data": [
        {
            "fid": "4D532E18D89E41028F4EF9E984CBDF59",
            "ffaxinny": "202411",
            "fgangweilb": "岗位类别1",
            "fxuhao": "30",
            "fyueshuo": 0.5,
            "fshenfenz": "123",
            "fxingming": "张三",
            "fbumen": "开发部",
            "岗位工资": "300",
            "fdanwei": "北京公司",
            "fgangwei": "岗位1-1",
            "工龄工资": "600"
        },
        {
            "fid": "09A48E5A2AB14F62A1A97AF0F5A2B6E6",
            "ffaxinny": "202412",
            "fgangweilb": "岗位类别2",
            "fxuhao": "31",
            "fyueshuo": 0.7,
            "fshenfenz": "456",
            "fxingming": "李四",
            "fbumen": "行政部",
            "岗位工资": "400",
            "fdanwei": "北京公司",
            "fgangwei": "岗位2-1",
            "工龄工资": "500"
        }
    ],
    "meta": {
        "sheetName": "Sheet1",
        "dataName": "/main/gongzidrlsb"
    }
}

使用案例

下面提供几个常用的案例

设置校验规则

导入时校验数据,不符合校验规则的数据,导入后会提示。校验规则分为列校验规则和行校验规则:

  • 列校验规则用于单列的校验,例如:“订单日期”列不允许为空
  • 行校验规则用于多列联合校验,例如:支付方式为人民币时,汇率为空,非人民币时,汇率不为空

校验规则不通过,只是不导入所在行数据,不是错误,不会触发错误回滚。在导入后,统一显示所有校验规则不通过的 Sheet、行、列信息,如下图所示

  • 不满足“列校验规则”提示:列:XXX,不满足校验条件
  • 不满足“行校验规则”提示:行:XX,不满足行校验条件:XXX

列校验规则

例如:不导入“订单日期”列为空的数据。在工作表列中添加“订单日期”列,设置“校验表达式”为:工作表当前列 != 空值,设计界面如下图所示

行校验规则

例如:支付方式为人民币时,汇率为空,非人民币时,汇率不为空。在工作表列中添加“支付方式”列和“汇率”列,添加一个行校验规则“汇率必填”,设置“规则表达式”为:支付方式 == 'RMB' || 汇率 != 空值,设计界面如下图所示

导入主从数据

  • 设置主工作表
    • “工作表名”设置为“XX 主表”
    • “类型”设置为“普通工作表”
    • “数据表”选择一个主数据集

  • 设置从工作表
    • 增加一个新的工作表,“工作表名”设置为“XX 从表”
    • “类型”设置为“普通工作表”
    • “数据表”选择一个从数据集

  • 设置主从关联

Excel 文件中的主表数据和从表数据,使用“订单编号”关联,如下图所示

  • 在“订单主表”工作表的“工作表列”中,添加“订单编号”列,如下图所示

  • 在“订单从表”工作表的“工作表列”中,添加“订单编号”列
    • “数据表列”选择从数据集的外键列
    • “主工作表”选择“订单主表”工作表
    • “主工作表查找列”选择“订单编号”列

  • 导入后的主表数据和从表数据,如下图所示

导入多列头

  • 设置“表头行数”和“列头行数”

Excel 文件的表头和列头,如下图所示

1728645378415

设置“表头行数”为3,“列头行数”为2,表头行数包括列头行数。选择导入的数据集,如下图所示

  • 设置多列头的列名

多列头的所有列,都需要在“工作表列”中定义,列名使用竖线 | 分割,两级中间使用一个竖线。“数据表列”选择数据集中的列,如下图所示

1728645471762

导入计算列

导入时,数据表列的值可以来源于计算表达式。例如:数据表中有此列,Excel 文件中没有,但是可以通过 Excel 中其他列的值(或者请求参数)计算出来。此时,设置该列的计算表达式即可。

  • 添加计算列

在“工作表列”中,添加列,将“数据表列”选择为“实付金额”

  • 设置计算规则

设置“计算表达式”,如下图所示

导入后的数据,如下图所示

获取导入批次

Excel 导入组件提供 Excel 导入上下文,包括“导入批次”和“导入序号”。

  • 导入批次:一次导入产生一个批次号,导入时,将批次号存入数据表。导入后获取本次导入的批次号,可以获取本次导入的数据
  • 导入序号:导入时,每条数据有一个序号,序号从1开始且递增

批次和序号都是导入 Excel 文件时,导入服务自动生成的,因此 Excel 文件中没有这两列,数据表中要有这两列

  • 导入批次和序号

在工作表列中添加列,“数据表列”选择“批次”,“计算表达式”选择“Excel 导入上下文-导入批次”,如下图所示

“序号”添加的方式与上述的“批次”添加方法一样,选择““Excel 导入上下文-导入序号”,添加完成后,如下图所示

导入后的批次和序号,如下图所示

  • 获取本次导入的批次和最大序号

在高级 Excel 导入组件的“导入成功”事件中,使用 event.detail.result.lastRow 获取本次导入的最后一条数据。根据工作表配置,本条数据中可能包含序号和批次,如下图所示

特别说明:

  • 上图中 lastRow 对象下的属性名 orderm,是数据集“订单主表”的名称
  • batch 和 orderNumber 是其中数据列的名称

导入自增主键

如果要导入的数据集的主键是自增主键,导入时需要注意

  • Excel 文件中如果包含主键列,需要清空其中的数据
  • 在“工作表列”中添加列,“数据表列”选择主键列,“计算表达式”设置为“空值”

前端传入参数

在导入服务中定义参数

在工作表配置中使用参数

在页面上的导入服务组件中,设置参数

取消默认提示

默认情况下,导入成功则提示“导入成功”,导入失败则提示出错原因。将“默认提示”属性设置为“否”,则不会提示

1738891104205

在“导入失败”事件中,通过 msg 可获取到出错原因,js 代码如下

let onImportExcel0Error = (event) => {
    let {detail:{msg}} = event;
    message.error("导入出错:" + msg);
}

案例位置

桌面-页面-文件处理组件-高级导入组件.w

results matching ""

    No results matching ""