6.1 文件与导入导出(改)

导入导出在一定程度上是企业级软件和效率工具(office工具)的桥梁

文件的上传下载以及业务数据的导入导出是企业级软件一个比较常规的需求,甚至是巨量的需求。业务有管理需要一般都伴随有导入导出需求,导入导出在一定程度上是企业级软件和效率工具(office工具)的桥梁。oinone的文件模块就提供了通用的导入导出实现方案,以简单、一致、可扩展为目标,简单是快速入门,一致是用户操作感知一致、可扩展是满足用户最大化的自定义需求。

下图为文件导入导出的实现示意图,大家可以做一个整体了解

6.1 文件与导入导出(改)

图6-1-1 文件导入导出实现示意图

一、基础能力

准备工作

  1. pamirs-demo-api的pom文件中引入pamirs-file2-api包依赖
        <dependency>
            <groupId>pro.shushi.pamirs.core</groupId>
            <artifactId>pamirs-file2-api</artifactId>
        </dependency>

图6-1-2 引入pamirs-file2-api包依赖

  1. DemoModule增加对FileModule的依赖
@Module(dependencies = {FileModule.MODULE_MODULE})

图6-1-3 DemoModule增加对FileModule的依赖

  1. pamirs-demo-boot的pom文件中引入pamirs-file2-core包依赖
        <dependency>
            <groupId>pro.shushi.pamirs.core</groupId>
            <artifactId>pamirs-file2-core</artifactId>
        </dependency>

图6-1-4 启动工程引入pamirs-file2-core包依赖

  1. pamirs-demo-boot的application-dev.yml文件中增加配置pamirs.boot.modules增加file,即在启动模块中增加file模块
pamirs:
    boot:
    modules:
        - file

图6-1-5 pamirs-demo-boot的application-dev.yml文件中增加配置

  1. pamirs-demo-boot的application-dev.yml文件中增加oss的配置。更多有关文件相关配置详见4.1.1【模块之yml文件结构详解】一文
cdn:
    oss:
    name: 阿里云
    type: OSS
    bucket: demo
    uploadUrl: #换成自己的oss上传服务地址
    downUrl: #换成自己的oss下载服务地址
    accessKeyId: #阿里云oss的accessKeyId
    accessKeySecret: #阿里云oss的accessKeySecret
    mairDir: upload/demo #换成自己的目录
    validTime: 360000
    timeout: 60000
    active: true
    referer: www.shushi.pro

图6-1-6 application-dev.yml文件中增加oss的配置

  1. 其他文件系统支持

a. 文件系统类型

类型 服务
OSS 阿里云
UPYUN 又拍云
MINIO Minio
HUAWEI_OBS 华为云
LOCAL 本地文件存储

表6-1-1 支持的文件系统类型

b. OSS配置示例

ⅰ. 华为云OBS

cdn:
  oss:
    name: 华为云
    type: HUAWEI_OBS
    bucket: pamirs #(根据实际情况修改)
    uploadUrl: obs.cn-east-2.myhuaweicloud.com
    downloadUrl: obs.cn-east-2.myhuaweicloud.com
    accessKeyId: 你的accessKeyId
    accessKeySecret: 你的accessKeySecret
    # 根据实际情况修改
    mainDir: upload/
    validTime: 3600000
    timeout: 600000
    active: true
    allowedOrigin: http://192.168.95.31:8888,https://xxxx.xxxxx.com
    referer:

图6-1-7 OBS的配置说明

ⅱ. MINIO

cdn:
  oss:
    name: minio
    type: MINIO
    bucket: pamirs #(根据实际情况修改)
    uploadUrl: http://192.168.243.6:32190 #(根据实际情况修改)
    downloadUrl: http://192.168.243.6:9000 #(根据实际情况修改)
    accessKeyId: 你的accessKeyId
    accessKeySecret: 你的accessKeySecret
    # 根据实际情况修改
    mainDir: upload/
    validTime: 3600000
    timeout: 600000
    active: true
    referer:
    localFolderUrl:

图6-1-8 MINIO的配置说明

ⅲ. 又拍云

cdn:
  oss:
    name: 又拍云
    type: UPYUN
    bucket: pamirs #(根据实际情况修改)
    uploadUrl: v0.api.upyun.com
    downloadUrl: v0.api.upyun.com
    accessKeyId: 你的accessKeyId
    accessKeySecret: 你的accessKeySecret
    # 根据实际情况修改
    mainDir: upload/
    validTime: 3600000
    timeout: 600000
    active: true
    referer: 

图6-1-9 又拍云的配置说明

ⅳ. 本地文件存储

cdn:
  oss:
    name: 本地文件NG系统
    type: LOCAL
    bucket: pamirs #(根据实际情况修改)
    # uploadUrl 这个是Oinone后端服务地址和端口
    uploadUrl: http://127.0.0.1:8091
    # downloadUrl前端地址,即直接映射在nginx的静态资源的路径和端口
    downloadUrl: http://127.0.0.1:8081
    validTime: 3600000
    timeout: 600000
    active: true
    referer:
    # 本地Nginx静态资源目录
    localFolderUrl: /Users/oinone/nginx/html/designer/static

图6-1-10 本地文件的配置说明

文件上传

文件上传下载是常用功能,也是导入导出功能的基础。接下的例子,我们来看看Oinone是如何玩转文件上传的功能的

@Field.String
@Field(displayName = "图片" ,multi = true)
@UxForm.FieldWidget(@UxWidget(widget = "Upload"))
private List<String> imgUrls;

Step1 修改PetTalent模型

为PetTalent模型增加一个字段imgUrls ,用于保存上传后的文件地址。同时定义Form视图的时候改字段的前端展示widget为“Upload”

<field data="imgUrls" widget="Upload"/>

Step2 修改PetTalent的Form视图

因为前面教程中为PetTalent的Form视图写了自定义视图,所以这里需要手工加上字段配置,不然展示不出来,同时也要加上widget="Upload"的配置,因为自定义视图不会再去读字段上定义的UxWidget,字段上的UxWidget只对默认视图生效。

Step3 重启看效果

image.png

默认导入导出功能

因为前面我们在Demo的启动工程中加入对pamirs-file2-core的jar包依赖同时启动模块列表中也增加了file模块,所以所有的默认表格页面都出现了默认的导入导出按钮。

image.png

但是我们发现宠物达人的表格就没有出现导入导出按钮,首先我们自定义了表格视图,而且在学习自定义Action的时候修改了表格视图的Action的填充方式为手工指定。

image.png

Step1 修改PetTalent的Table视图

修改PetTalent的自定义Table视图,把表格视图的Action的填充方式改为自动填充,或者根据平台默认前端动作增加导入、导出两个前端动作

<template slot="actions" autoFill="true">
  <!--        <action actionType="client" name="demo.doNothing" label="第一个自定义Action" />-->
  <!--        <action name="delete" label="删除" />-->
  <!--        <action name="redirectCreatePage" label="创建" />-->
</template>
<template slot="actions" autoFill="true">
  <action actionType="client" name="demo.doNothing" label="第一个自定义Action" />
  <action name="delete" label="删除" />
  <action name="redirectCreatePage" label="创建" />
<!--   <action name="$$internal_GotoListExportDialog" label="导出" />
  <action name="$$internal_GotoListImportDialog" label="导入" /> -->
    <action name="internalGotoListExportDialog" label="导出" />
    <action name="internalGotoListImportDialog" label="导入" />
</template>

Step2 重启看效果

image.png

Step3 注意点

  1. 系统生成的导入模版以默认的Form视图为准

  2. 系统生成的导出模版以默认的Table视图为准

  3. 有自定义导入导出模版后,默认模版会失效

自定义导入导出模版与逻辑

数据的导出(举例一)

  1. 通常我们只需定义一个导出模版

  2. 默认导出方法是调用模型的queryPage函数(即在日常开发中我们经常会自定义模型的queryPage函数),为了跟页面查询效果保持一致,默认导出方式实现中手工生效了hook,所以类似数据权限的hook都会生效,与页面查询逻辑一致。

  3. 如果不一致可以实现ExcelExportFetchDataExtPoint扩展点来完成,在举例二中介绍

Step1 新建PetTalentExportTemplate

新建PetTalentExportTemplate类实现ExcelTemplateInit接口。

  1. 实现ExcelTemplateInit接口,系统会自动调用generator方法,并注册该方法返回的模版定义列表,例子中定义了一个导出模版“宠物达人导出”

  2. 关联对象为集合,petShops[*].shopName

  3. 关联对象为模型,creater.name

package pro.shushi.pamirs.demo.core.init.exports;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.model.PetTalent;
import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum;
import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition;
import pro.shushi.pamirs.file.api.util.ExcelHelper;
import pro.shushi.pamirs.file.api.util.ExcelTemplateInit;

import java.util.Collections;
import java.util.List;

@Component
public class PetTalentExportTemplate implements ExcelTemplateInit {
    public static final String TEMPLATE_NAME ="宠物达人导出";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        //可以返回多个模版,导出的时候页面上由用户选择导出模版
        return Collections.singletonList(
                ExcelHelper.fixedHeader(PetTalent.MODEL_MODEL,TEMPLATE_NAME)
                        .createBlock(TEMPLATE_NAME,PetTalent.MODEL_MODEL)
                        .setType(ExcelTemplateTypeEnum.EXPORT)
                        .addColumn("name","名称")
                        .addColumn("annualIncome","年收入")
                        .addColumn("petShops[*].shopName","店铺名")
                        .addColumn("creater.name","创建者")
                        .build());
    }

}

Step2 重启看效果

  1. 点击【宠物达人】菜单进入宠物达人列表页,选择记录点击【导出】按钮。导出规则为

a. 如果勾选列表记录行,则只导出勾选记录

b. 如果没有勾选,则根据查询条件进行导出

image.png

  1. 在导出弹出框中选择导出模版,点击【导出】按钮提示成功,则表示导出任务被正常创建

image.png

  1. 切换换模块,进入【文件】应用,点击【导出任务】菜单查看导出任务情况。分别对应三种情况:

a. Table视图中带来条件但查询结果没有数据,则会报错

b. Table视图中带来条件或无条件但查询结果有数据,则会成功

c. Table视图中勾选了数据行,则只导出勾选数据行对应数据

image.png

数据的导出(举例二)

如果您有特殊需求,请实现ExcelExportFetchDataExtPoint扩展点来完成,用这种模式请自行控制数据权限,小心小心再小心。因为数据权限是通过hook机制来完成控制,在面向切面-拦截器一文中有说明拦截器只有在前端请求时才会默认生效,如果后端代码自行调用需要生效hook机制需要根据函数之元位指令一文手动去生效。

下面例子中为了复用了ExcelExportSameQueryPageTemplate的逻辑主要是hook的机制,ExcelExportSameQueryPageTemplate手工生效了hook,例子中继承了ExcelExportSameQueryPageTemplate类,只做返回结果的后置处理

Step1 修改PetTalentExportTemplate

package pro.shushi.pamirs.demo.core.init.exports;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.model.PetTalent;
import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext;
import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum;
import pro.shushi.pamirs.file.api.extpoint.ExcelExportFetchDataExtPoint;
import pro.shushi.pamirs.file.api.extpoint.impl.ExcelExportSameQueryPageTemplate;
import pro.shushi.pamirs.file.api.model.ExcelExportTask;
import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition;
import pro.shushi.pamirs.file.api.util.ExcelHelper;
import pro.shushi.pamirs.file.api.util.ExcelTemplateInit;
import pro.shushi.pamirs.meta.annotation.ExtPoint;

import java.util.Collections;
import java.util.List;

@Component
public class PetTalentExportTemplate extends ExcelExportSameQueryPageTemplate implements ExcelTemplateInit , ExcelExportFetchDataExtPoint {
    public static final String TEMPLATE_NAME ="宠物达人导出";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        //可以返回多个模版,导出的时候页面上由用户选择导出模版
        return Collections.singletonList(
                ExcelHelper.fixedHeader(PetTalent.MODEL_MODEL,TEMPLATE_NAME)
                        .createBlock(TEMPLATE_NAME,PetTalent.MODEL_MODEL)
                        .setType(ExcelTemplateTypeEnum.EXPORT)
                        .addColumn("name","名称")
                        .addColumn("annualIncome","年收入")
                        .addColumn("petShops[*].shopName","店铺名")
                        .addColumn("creater.name","创建者")
                        .build());
    }

    //表达式里的常量需要使用" "包括
    @Override
    @ExtPoint.Implement(expression = "context.model == " + PetTalent.MODEL_MODEL+" && context.name == " +TEMPLATE_NAME+"" )
    public List<Object> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) {
        //复用父类的权限控制等
        List<Object> result =  super.fetchExportData(exportTask,context);
        //对petTalents做一些操作
        List<PetTalent>  petTalents =  (List<PetTalent>)result.get(0);
        for(PetTalent petTalent:petTalents){
            petTalent.setName(petTalent.getName()+":被修改过");
        }
        return result;
    }
}

Step2 重启看效果

相同的操作,发现导出文件内容被特定修改过

image.png

数据的导入(举例一)

Step1 创建PetTalentImportTemplate

新建PetTalentImportTemplate类实现ExcelImportDataExtPoint和ExcelTemplateInit接口。

  1. 实现ExcelTemplateInit接口,系统会自动调用generator方法,并注册该方法返回的模版定义列表,例子中定义了一个导入模版“宠物达人导入”

a. 通过ExcelCellDefinition自定义单元可选值,并在addColumn时绑定如:.addColumn("petTalentSex",sexExcelCellDefinition)

b. 通过addUnique,设置导入时的唯一key,相同key同时相连行会进行合并操作

  1. 实现ExcelImportDataExtPoint的importData扩展点,实现自定义的导入逻辑

a. 声明扩展点的生效规则:@ExtPoint.Implement(expression = "importContext.definitionContext.model == \" + PetTalent.MODEL_MODEL+"\ && importContext.definitionContext.name == \" +TEMPLATE_NAME+"\" ) 。可以用表达式的上下文就是方法入参

b. 记录错误:importTask.addTaskMessage(TaskMessageLevelEnum.ERROR,"达人名称不能为空")。

c. importData方法返回true,代表继续下一行的导入。返回false则表示中断导入

package pro.shushi.pamirs.demo.core.init.imports;

import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import pro.shushi.pamirs.demo.api.enumeration.PetTalentSexEnum;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.demo.api.model.PetTalent;
import pro.shushi.pamirs.file.api.context.ExcelImportContext;
import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum;
import pro.shushi.pamirs.file.api.enmu.ExcelValueTypeEnum;
import pro.shushi.pamirs.file.api.enmu.TaskMessageLevelEnum;
import pro.shushi.pamirs.file.api.extpoint.AbstractExcelImportDataExtPointImpl;
import pro.shushi.pamirs.file.api.extpoint.ExcelImportDataExtPoint;
import pro.shushi.pamirs.file.api.model.ExcelCellDefinition;
import pro.shushi.pamirs.file.api.model.ExcelImportTask;
import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition;
import pro.shushi.pamirs.file.api.util.ExcelHelper;
import pro.shushi.pamirs.file.api.util.ExcelTemplateInit;
import pro.shushi.pamirs.framework.connectors.data.sql.Pops;
import pro.shushi.pamirs.framework.connectors.data.tx.transaction.Tx;
import pro.shushi.pamirs.meta.annotation.ExtPoint;
import pro.shushi.pamirs.meta.api.dto.config.TxConfig;
import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper;

import java.util.*;

@Component
public class PetTalentImportTemplate extends AbstractExcelImportDataExtPointImpl<PetTalent> implements ExcelTemplateInit , ExcelImportDataExtPoint<PetTalent> {
    public static final String TEMPLATE_NAME ="宠物达人导入";

    @Override
    public List<ExcelWorkbookDefinition> generator() {
        //自定义定义Excel单元格,设置单元可选值
        Map<String,String> sexType = new HashMap<>();
        sexType.put(String.valueOf(PetTalentSexEnum.FEMAL.value()),PetTalentSexEnum.FEMAL.displayName());
        sexType.put(String.valueOf(PetTalentSexEnum.MAN.value()),PetTalentSexEnum.MAN.displayName());
        ExcelCellDefinition sexExcelCellDefinition = new ExcelCellDefinition();
        sexExcelCellDefinition.setType(ExcelValueTypeEnum.ENUMERATION).setValue("性别").setFormat(JSON.toJSONString(sexType));

        //可以返回多个模版,导出的时候页面上由用户选择导出模版
        return Collections.singletonList(
                ExcelHelper.fixedHeader(PetTalent.MODEL_MODEL,TEMPLATE_NAME)
                        .createBlock(TEMPLATE_NAME,PetTalent.MODEL_MODEL)
                        .setType(ExcelTemplateTypeEnum.IMPORT)
                        //相同key,相连行会进行合并操作
                        .addUnique(PetTalent.MODEL_MODEL,"name")
                        .addColumn("name","名称")
                        .addColumn("annualIncome","年收入")
                        .addColumn("petShops[*].shopName","店铺名")
                        .addColumn("petTalentSex",sexExcelCellDefinition)
                        .build());
    }

    //表达式里的常量需要使用" "包括
    @Override
    @ExtPoint.Implement(expression = "importContext.definitionContext.model == \"" + PetTalent.MODEL_MODEL+"\" && importContext.definitionContext.name == \"" +TEMPLATE_NAME+"\"" )
    public Boolean importData(ExcelImportContext importContext, PetTalent data) {
        ExcelImportTask importTask = importContext.getImportTask();
        if(StringUtils.isBlank(data.getName())){
            //返回true表示继续读下一行。返回false表示中断
            importTask.addTaskMessage(TaskMessageLevelEnum.ERROR,"达人名称不能为空");
            return Boolean.TRUE;
        }
        if(CollectionUtils.isNotEmpty(data.getPetShops())){
            List<String > petShopNams = new ArrayList<>();
            for(PetShop petShop:data.getPetShops()){
                petShopNams.add(petShop.getShopName());
            }
            //FIXME 抽到Service中去,散落的查询逻辑不好维护。
            IWrapper<PetShop> queryWrapper = Pops.<PetShop>lambdaQuery().from(PetShop.MODEL_MODEL).in(PetShop::getShopName, petShopNams);
            List<PetShop> shops = new PetShop().queryList(queryWrapper);
            data.setPetShops(shops);
        }
        Tx.build(new TxConfig().setPropagation(Propagation.REQUIRED.value())).executeWithoutResult(status -> {
            data.create();
            data.fieldSave(PetTalent::getPetShops);
        });
        return Boolean.TRUE;
    }

}

Step2 重启看效果

  1. 点击【宠物达人】菜单进入宠物达人列表页面,点击【导入】。

  2. 在导入弹出框中选择导入模版后点击下载模版,并提按模版填写数据。

  3. 在导入弹出框中点击【点击上传】按钮,上传填写好的文件,并点击【导入】按钮

image.png

image.png

  1. 列表页增加对应数据行,点击详情可以看到所有相关数据

image.png

这个例子中用了ExcelHelper这个简单工具类来定义模版,基本能满足日常需求。复杂的可以直接用WorkbookDefinitionBuilder来完成。

导入导出的更多配置项

基础设计

以POI和EasyExcel共有的Excel处理能力为基础进行设计。

  1. 以Workbook为核心的模板定义

  2. 支持多个Sheet定义。

  3. 支持【固定表头】形式的模板定义。(陆续支持【固定格式】和【混合格式】)

  4. 支持三个维度的定义方式:表头定义(列定义),行定义,单元格定义。

  5. 支持单元格样式、字体样式等Excel样式定义。

  6. 支持合并单元格。

  7. 支持数字、字符串、时间、日期、公式等Excel的数据处理能力。

  8. 支持直接存数据库和获取导入数据两种方式。

Excel功能

模板定义,使用数据初始化或页面操作的方式创建Excel模板。

导出任务的创建,页面选择导出模板、导出条件后,点击确定后,创建导出任务。导出方式为:异步,在导出任务页面查看导出任务,并允许多次下载。

导入任务的创建,页面选择模板,并点击下载后,获取对应Excel模板。按照指定规则(平台默认规则或业务自定义规则)填写完成后,上传Excel文件进行异步导入操作。

Excel工作簿(ExcelWorkbookDefinition)

属性 类型 名称 必填 默认值 说明
name String 名称 Excel工作簿的定义名称
filename String 文件名 导出时使用的文件名,不指定则默认使用名称作为文件名
version Enum Office版本 AUTO OfficeVersionEnum
sheetList List<ExcelSheetDefinition>(JSONArray) 工作表定义列表 对多个Sheet进行定义

Excel工作表(ExcelSheetDefinition)

属性 类型 名称 必填 默认值 说明
name String 工作表名称 指定工作表名称,当未指定工作表名称时,默认使用模型的显示名称,未绑定模型生成时,默认使用「Sheet + ${index}」作为工作表名称
blockDefinitionList List<ExcelBlockDefinition> 区块列表 至少一个区块
mergeRangeList List<ExcelCellRangeDefinition> 单元格合并范围 工作表的合并范围相对于A1单元格行列索引,绝对定义
uniqueDefinitions List<ExcelUniqueDefinition> 唯一定义 全局模型唯一定义,当区块中未定义时,使用该定义

Excel区块(ExcelBlockDefinition)

属性 类型 名称 必填 默认值 说明
designRegion ExcelCellRangeDefinition 设计区域 设计区域是区块的唯一标识,不允许重叠。定义时应示意扩展方向,如下图所示。
analysisType Enum 解析类型 FIXED_HEADER ExcelAnalysisTypeEnum
usingCascadingStyle Boolean 是否使用层叠样式 false 将样式覆盖变为样式层叠;单个工作表有效;优先级顺序为:列样式 < 行样式 < 单元格样式三个属性共同定义一个Sheet的样式
headerList List<ExcelHeaderDefinition> 表头定义 【固定表头】类型下,至少一行,不显示的表头行使用配置行定义。
rowList List<ExcelRowDefinition> 行定义 【固定格式】类型下,至少一行。
bindingModel String 绑定模型 绑定任意模型,允许使用类的全限定名。
mergeRangeList List<ExcelCellRangeDefinition> 单元格合并范围 区块内的单元格合并范围相对与区块起始行列索引。当合并范围指定到最后一行的表头定义时,属于相对定义,即扩展时会自动填充合并样式。否则与工作表合并相同。
uniqueDefinitions List<ExcelUniqueDefinition> 唯一定义 默认使用模型定义的unique属性

Excel行(ExcelRowDefinition)

属性 类型 名称 必填 默认值 说明
cellList List<ExcelCellDefinition> 单元格列表
style ExcelStyleDefinition 样式 行样式

Excel表头(ExcelHeaderDefinition继承ExcelRowDefinition)

属性 类型 名称 必填 默认值 说明
cellList List<ExcelCellDefinition> 单元格列表
style ExcelStyleDefinition 样式 行样式
direction Enum 排列方向 HORIZONTAL ExcelDirectionEnum
selectRange ExcelCellRangeDefinition 应用范围 仅起始属性有效
isConfig Boolean 是否配置表头
autoSizeColumn Boolean 自动列宽 true 多行表头需使用配置表头进行定义
isFrozen Boolean 是否冻结 Excel冻结功能

Excel单元格范围(ExcelCellRangeDefinition)

属性 类型 名称 必填 默认值 说明
beginRowIndex Integer 起始行索引
endRowIndex Integer 结束行索引
beginColumnIndex Integer 起始列索引
endColumnIndex Integer 结束列索引
fixedBeginRowIndex Boolean 固定起始行索引
fixedEndRowIndex Boolean 固定结束行索引
fixedBeginColumnIndex Boolean 固定起始列索引
fixedEndColumnIndex Boolean 固定结束列索引

Excel单元格(ExcelCellDefinition)

属性 类型 名称 必填 默认值 说明
field String 属性 当且仅当单元格在表头中有效;多行表头需使用配置表头进行定义;多个配置表头定义属性时,仅首个属性定义生效;
value String
type Enum 值类型 STRING ExcelValueTypeEnum
formatContextString String(JSONObject) 格式化上下文 针对不同类型的值设置不同的参数内容(类型待定)
isStatic Boolean 是否是静态值 false 静态值在数据解析时将使用配置值,不读取单元格的值
style ExcelStyleDefinition 样式 单元格样式

Excel单元格样式(ExcelStyleDefinition)

属性 类型 名称 必填 默认值 说明
horizontalAlignment Enum 水平对齐 GENERAL ExcelHorizontalAlignmentEnum
verticalAlignment Enum 垂直对齐 ExcelVerticalAlignmentEnum
fillBorderStyle Enum 全边框样式 NONE ExcelBorderStyleEnum
topBorderStyle Enum 上边框样式 ExcelBorderStyleEnum
rightBorderStyle Enum 右边框样式 ExcelBorderStyleEnum
bottomBorderStyle Enum 下边框样式 ExcelBorderStyleEnum
leftBorderStyle Enum 左边框样式 ExcelBorderStyleEnum
wrapText Boolean 是否自动换行 true
typefaceDefinition ExcelTypefaceDefinition 字体
width Integer 首列样式中有效
height Integer 首列样式中有效

Excel字体(ExcelTypefaceDefinition)

属性 类型 名称 必填 默认值 说明
typeface Enum 字体 ExcelTypefaceEnum

注意事项

如果是在分布式模式下,启动工程中没有加入File模块,则需要手工调用ExcelTemplateInitHelper .init方法

Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9325.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
史, 昂的头像史, 昂数式管理员
上一篇 2024年5月23日 am8:13
下一篇 2024年5月23日

相关推荐

  • 第5章 Oinone的CDM

    2024年5月23日
    1.6K00
  • 3.5.2.2 构建View的Template

    我们在很多时候需要自定义模型的管理页面,而不是直接使用默认页面,比如字段的展示与隐藏,Action是否在这个页面上出现,搜索条件自定义等等,那么本章节带您一起学习如何自定义View的Template。 自定义View的Template 在使用默认layout的情况下,我们来做几个自定义视图Template,并把文件放到指定目录下。 图3-5-2-14 自定义View的Template 第一个Tabel Step1 自定义PetTalent的列表 我们先通过数据库查看默认页面定义,找到base_view表,过滤条件设置为model =\’demo.PetTalent\’,我们就看到该模型下对应的所有view,这些是系统根据该模型的ViewAction对应生成的默认视图,找到类型为【表格(type = TABLE)】的记录,查看template字段。 图3-5-2-15 base_view表查看template字段 <view name="tableView" cols="1" type="TABLE" enableSequence="false"> <template slot="actions" autoFill="true"/> <template slot="rowActions" autoFill="true"/> <template slot="fields"> <field invisible="true" data="id" label="ID" readonly="true"/> <field data="name" label="达人"/> <field data="dataStatus" label="数据状态"> <options> <option name="DRAFT" displayName="草稿" value="DRAFT" state="ACTIVE"/> <option name="NOT_ENABLED" displayName="未启用" value="NOT_ENABLED" state="ACTIVE"/> <option name="ENABLED" displayName="已启用" value="ENABLED" state="ACTIVE"/> <option name="DISABLED" displayName="已禁用" value="DISABLED" state="ACTIVE"/> </options> </field> <field data="createDate" label="创建时间" readonly="true"/> <field data="writeDate" label="更新时间" readonly="true"/> <field data="createUid" label="创建人id"/> <field data="writeUid" label="更新人id"/> </template> <template slot="search" autoFill="true" cols="4"/> </view> 图3-5-2-16 base_view表查看template字段 对比view的template定义与页面差异,从页面上看跟view的定义少了,创建人id和更新人id。因为这两个字段元数据定义里invisible属性。 a. 当XML里面没有配置,则用元数据覆盖了。 b. 当XML里面配置了,则不会用元数据覆盖了。 在下一步中我们只要view的DSL中给这两个字段加上invisible="false"就可以展示出来了 图3-5-2-17 查看列表展示 图3-5-2-18 invisible属性 新建pet_talent_table.xml文件放到对应的pamirs/views/demo_core/template目录下,内容如下 a. 对比默认视图,在自定义视图时需要额外增加属性model="demo.PetTalent" b. name设置为"tableView",系统重启后会替换掉base_view表中model为"demo.PetTalent",name为"tableView",type为"TABLE"的数据记录。 ⅰ. name不同的但type相同,且viewAction没有指定时,根据优先级priority进行选择。小伙伴可以尝试修改name="tableView1",并设置priority为1,默认生成的优先级为10,越小越优先。 ccreateUid和writeUid字段,增加invisible="false"的属性定义 <view name="tableView" model="demo.PetTalent" cols="1" type="TABLE" enableSequence="false"> <template slot="actions" autoFill="true"/> <template slot="rowActions" autoFill="true"/> <template slot="fields"> <field invisible="true" data="id" label="ID" readonly="true"/> <field data="name" label="达人"/> <field data="dataStatus" label="数据状态"> <options> <option name="DRAFT" displayName="草稿" value="DRAFT" state="ACTIVE"/> <option name="NOT_ENABLED" displayName="未启用" value="NOT_ENABLED" state="ACTIVE"/> <option name="ENABLED" displayName="已启用" value="ENABLED" state="ACTIVE"/> <option name="DISABLED" displayName="已禁用" value="DISABLED" state="ACTIVE"/> </options> </field> <field data="createDate" label="创建时间" readonly="true"/> <field data="writeDate" label="更新时间" readonly="true"/> <field data="createUid" label="创建人id" invisible="false"/> <field data="writeUid" label="更新人id" invisible="false"/> </template> <template slot="search" autoFill="true" cols="4"/> </view> 图3-5-2-19 增加invisible=”false”的属性定义 Step2 重启应用看效果 图3-5-2-20 示例效果 第一个Form Step1 自定义PetTalent的编辑页…

    2024年5月23日
    1.2K00
  • 3.3.6 枚举与数据字典

    枚举是大家在系统开发中经常用的一种类型,在oinone中也对枚举类型进行了支持,同时也做了相应的加强。希望通过本文能让大家对枚举的使用,有更全面的认知 一、枚举系统与数据字典 枚举是列举出一个有穷序列集的所有成员的程序。在元数据中,我们使用数据字典进行描述。 协议约定 枚举需要实现IEnum接口和使用@Dict注解进行配置,通过配置@Dict注解的dictionary属性来设置数据字典的唯一编码。前端使用枚举的displayName来展示,枚举的name来进行交互;后端使用枚举的value来进行交互(包括默认值设置也使用枚举的value)。 枚举会存储在元数据的数据字典表中。枚举分为两类:1.异常类;2.业务类。异常类枚举用于定义程序中的错误提示,业务类枚举用于定义业务中某个字段值的有穷有序集。 编程式用法 图3-3-6-1 编程式用法 如果一个字段的类型被定义为枚举,则该字段就可以使用该枚举来进行可选项约束(options)。该字段的可选项为枚举所对应数据字典的子集。 可继承枚举 继承BaseEnum可以实现java不支持的继承枚举。同时可继承枚举也可以用编程式动态创建枚举项。 可继承枚举也可以兼容无代码枚举。 图3-3-6-2 可继承枚举 二进制枚举 可以通过@Dict注解设置数据字典的bit属性或者实现BitEnum接口来标识该枚举值为2的次幂。 二、enum不可继承枚举(举例) 我们在介绍抽象基类中AbstractDemoCodeModel和AbstractDemoIdModel就引入了数据状态(DataStatusEnum)字段,并设置了必填和默认值为DISABLED。DataStatusEnum实现了IEnum接口,并用@Dict(dictionary = DataStatusEnum.dictionary, displayName = "数据状态")进行了注解。为什么不能继承呢?因为JAVA语言的限制导致enum是不可继承的 package pro.shushi.pamirs.core.common.enmu; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.IEnum; @Dict(dictionary = DataStatusEnum.dictionary, displayName = "数据状态") public enum DataStatusEnum implements IEnum<String> { DRAFT("DRAFT", "草稿", "草稿"), NOT_ENABLED("NOT_ENABLED", "未启用", "未启用"), ENABLED("ENABLED", "已启用", "已启用"), DISABLED("DISABLED", "已禁用", "已禁用"); public static final String dictionary = "partner.DataStatusEnum"; private String value; private String displayName; private String help; DataStatusEnum(String value, String displayName, String help) { this.value = value; this.displayName = displayName; this.help = help; } public String getValue() { return value; } public String getDisplayName() { return displayName; } public String getHelp() { return help; } } 图3-3-6-3 不可继承枚举 三、BaseEnum可继承枚举(举例) Step1 新增CatShapeExEnum继承CatShapeEnum枚举 package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; @Dict(dictionary = CatShapeExEnum.DICTIONARY,displayName = "萌猫体型Ex") public class CatShapeExEnum extends CatShapeEnum { public static final String DICTIONARY ="demo.CatShapeExEnum"; public final static CatShapeExEnum MID =create("MID",3,"中","中"); } 图3-3-6-4 新增CatShapeExEnum继承CatShapeEnum枚举 Step2 修改PetCatType的shape字段类型为CatShapeExEnum package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.CatShapeExEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.MultiTableInherited(type = PetCatType.KIND_CAT) @Model.model(PetCatType.MODEL_MODEL) @Model(displayName="萌猫品种",labelFields = {"name"}) public class PetCatType extends PetType { public static final String MODEL_MODEL="demo.PetCatType"; public static final String KIND_CAT="CAT"; @Field(displayName = "宠物分类",defaultValue…

    2024年5月23日
    3.3K00
  • 开发者

    1.开发者 1.1 Webhook 通过webhook流程节点可以向第三方系统发送请求。 1.1.1 请求方式 支持GET、POST两种请求方式。 1.1.2 URL 在Webhook URL中填写发送请求的HTTP地址。 1.1.3 Headers&Body Headers的value支持通过表达式配置变量 Body的数据类型支持KEY_VALUE和APPLICATION_JSON两种。

    2024年6月20日
    1.7K00

Leave a Reply

登录后才能评论