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日

相关推荐

  • 书籍封面

    2024年5月23日
    2.6K00
  • 5.6 商业支撑之商品域

    一、基础介绍 当业务在线化后,用于内部管理的产品主数据,叠加一堆销售属性变成了商品被推倒了前台,成为导购链路中最最重要的信息载体。看似最基础和最简单的商品模块也有很多门道。主要集中在以下几个方面: 商品的属性如何管理、呈现、参与导购(类目、搜索的过滤条件) 如何解决固定不变的内部管理需求与基于销售特性长期变化的运营需求之间的矛盾 在多渠道情况渠道商品,如何映射到实际sku进行履约 二、模型介绍 图5-6-1 模型介绍 类目属性,解决“商品的属性如何管理、呈现、参与导购(类目、搜索的过滤条件)” 前后台类目设计,解决“如何解决固定不变的内部管理需求与基于销售特性长期变化的运营需求之间的矛盾” 销售Sku和库存Sku设计,解决“在多渠道情况渠道商品,如何映射到实际sku进行履约” 要把这些问题搞清楚,得先把名词统一下: 领域 名称 oinone的定义 说明 举例 平台运营视角 Spu Product –>Spu2.1.9 –> 3.0.0 SPU(Standard Product Unit):标准化产品单元。SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性 iPhone X可以确定一个产品 后台类目 后台类目(Category) 商品分类分级管理,以及规范该类目下公共属性可以分为普通属性、销售属性 比如类目:3c数码/手机销售属性:内存大小、颜色等普通属性:分辨率 前台类目 前台类目(FrontCategory) 平台导购类目 通过前台类目关联后台类目或后台类目属性,用于满足运营需求 大体上SPU处于最上层、Item属于下一级,而SKU属于最低一层。SPU是平台层面,Item是商家层面,SKU是商家的Item确定销售属性SPU非必须,在平台类交易中,平台方为了规范商家发布商品信息,进行统一运营时需要 商家销售视角 Item 渠道商品(Item) 简单来说是:SPU加上归属商家、以及商家自有的价格与描述 商家A的iPhone X Sku 销售Sku(SaleSku) SKU=Stock Keeping Unit(库存保有单位)。是对每一个产品和服务的唯一标示符,该系统的使用SKU的值根于数据管理,使公司能够跟踪系统,如仓库和零售商店或产品的库存情况。 iPhone X 64G 银色 则是一个SKU。 店铺类目 ShopCategory 商家店铺导购类目 在平台类电商,商家都会有自己独立的店铺主页,商家类目跟前台类目作用类似,只是局限影响范围为商家店铺内 销售SKU中会有一个InvSkuCode来关联InventorySku,比如:品牌上在不同渠道(淘宝、京东、自建电商)中会有不同的销售SKU,在从渠道同步销售SKU会根据外部code 商家管理视角 产品或库存Sku InventorySku 跟销售领域的sku的定义类似,但销售领域是为了规范购买行为,这里规范企业内部管理。 iPhone X 64G 银色 组合Sku InventorySkuComposition 空调有内外机组合而成,这就是一个组合sku 产品分类 ProductKind 企业内部管理划分 商品系列 ItemSeries 指互相关联或相似的产品,是按照一定的分类标准对企业生产经营的全部产品进行划分的结果。一个产品系列内往往包括多个产品项目。产品系列的划分标准有产品功能、消费上的连带性、面向的顾客群、分销渠道、价格范围等 存货类别 StorageKind 为了反映存货的组成内容,正确计算产品的生产成本以及销售成本,会计上必须对存货进行科学地分类,按存货的不同类别进行核算 表5-6-1 各领域名称说明

    2024年5月23日
    78700
  • 业务域

    1. 业务域介绍 业务域是根据业务域对集成应用、开放接口进行归类管理。在创建集成应用、开发接口时,可选择归属的业务域。 操作入口:集成设计器——业务域。 2. 业务域管理 业务域管理提供新增、删除、搜索操作。 2.1 业务域列表 支持按照编码、名称、描述搜索业务域。 2.2 新增业务域 新增业务域:输入业务域名称、描述新增。 2.3 删除业务域 当前业务域未被其他数据记录引用时,可删除成功,反之如果被引用了,不允许删除。

    2024年6月20日
    94500
  • 文件

    文件应用下包含“导入/导出模版、导入任务、导出任务”三个菜单。其中导入/导出任务菜单比较常用。 导入/导出模版 当前版本会为租户的表格视图自动创建导出模版,此处可进行编辑、查看详情的操作。 导入任务 导入任务可以下载导入文档,点击详情可以查看该条记录的导入结果,任务信息分组中可以查看错误信息。 导出任务 和导入任务一致,导出任务菜单中可以下载导出文档,点击详情可以查看该条记录的导出结果,任务信息分组中可以查看错误信息。

    2024年6月20日
    72900
  • 4.2.2 框架之MessageHub

    一、MessageHub 请求出现异常时,提供”点对点“的通讯能力 二、何时使用 错误提示是用户体验中特别重要的组成部分,大部分的错误体现在整页级别,字段级别,按钮级别。友好的错误提示应该是怎么样的呢?我们假设他是这样的 与用户操作精密契合 当字段输入异常时,错误展示在错误框底部 按钮触发服务时异常,错误展示在按钮底部 区分不同的类型 错误 成功 警告 提示 调试 简洁易懂的错误信息 在oinone平台中,我们怎么做到友好的错误提示呢?接下来介绍我们的MessageHub,它为自定义错误提示提供无限的可能。 三、如何使用 订阅 import { useMessageHub, ILevel } from "@kunlun/dependencies" const messageHub = useMessageHub('当前视图的唯一标识'); /* 订阅错误信息 */ messageHub.subscribe((errorResult) => { console.log(errorResult) }) /* 订阅成功信息 */ messageHub.subscribe((errorResult) => { console.log(errorResult) }, ILevel.SUCCESS) 图4-2-2-1 订阅的示例代码 销毁 /** * 在适当的时机销毁它 * 如果页面逻辑运行时都不需要销毁,在页面destroyed是一定要销毁,重要!!! */ messageHub.unsubscribe() 图4-2-2-2 销毁的示例代码 四、实战 让我们把3.5.7.5【自定义视图-表单】一文中的自定义表单进行改造,加入我们的messageHub,模拟在表单提交时,后端报错信息在字段下方给予提示。 Step1 (后端)重写PetType的创建函数 重写PetType的创建函数,在创建逻辑中通过MessageHub返回错误信息,返回错误信息的同时要设置paths信息方便前端处理 @Action.Advanced(name = FunctionConstants.create, managed = true) @Action(displayName = "确定", summary = "创建", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.create) @Function.fun(FunctionConstants.create) public PetType create(PetType data){ List<Object> paths = new ArrayList<>(); paths.add("demo.PetType"); paths.add("kind"); PamirsSession.getMessageHub().msg(new Message().msg("kind error").setPath(paths).setLevel(InformationLevelEnum.ERROR).setErrorType(ErrorTypeEnum.BIZ_ERROR)); List<Object> paths2 = new ArrayList<>(); paths2.add("demo.PetType"); paths2.add("name"); PamirsSession.getMessageHub().msg(new Message().msg("name error").setPath(paths2).setLevel(InformationLevelEnum.ERROR).setErrorType(ErrorTypeEnum.BIZ_ERROR)); // data.create(); return data; } 图4-2-2-3 (后端)重写PetType的创建函数 Step 2 修改PetForm.vue <template> <div class="petFormWrapper"> <form :model="formState" @finish="onFinish"> <a-form-item label="品种种类" id="name" name="kind" :rules="[{ required: true, message: '请输入品种种类!', trigger: 'focus' }]"> <a-input v-model:value="formState.kind" @input="(e) => onNameChange(e, 'kind')" /> <span style="color: red">{{ getServiceError('kind') }}</span> </a-form-item> <a-form-item label="品种名" id="name" name="name" :rules="[{ required: true, message: '请输入品种名!', trigger: 'focus' }]"> <a-input v-model:value="formState.name" @input="(e) => onNameChange(e, 'name')" /> <span style="color: red">{{ getServiceError('name') }}</span> </a-form-item> </form> </div> </template>- <script lang="ts"> import { defineComponent, reactive…

    2024年5月23日
    81400

Leave a Reply

登录后才能评论