如何实现在引出时修改单据列表引出数据和Excel字体样式原创
金蝶云社区-吴锐雄
吴锐雄
25人赞赏了该文章 7,039次浏览 未经作者许可,禁止转载编辑于2022年11月29日 16:51:42
summary-icon摘要由AI智能服务提供

该文本主要描述了在一个没有内置插件的环境下,如何自定义实现数据导出功能,包括引出时修改数据样式、组合多个单据数据导出、设置单元格样式等需求。提出了通过XSSFWorkbook类来实现Excel样式的自定义,并详细描述了实现步骤,包括创建Java插件、在单据列表工具栏添加按钮、编写插件代码等。插件代码部分涵盖了从获取已选数据、构造XSSFWorkbook工作簿、写入数据到单元格、设置样式、构造输出流、转换输入流、上传到文件服务器的整个流程。特别指出了使用QueryServiceHelper查询其他单据数据的方法,以及如何通过XSSFWorkbook的API来设置单元格的高度、背景色、文字字体、颜色、字号等样式。最后,还展示了具体实现过程中的代码片段,包括如何获取数据、构造工作簿和单元格、数据填充、输出流处理等关键步骤。

关键词:引出,单据列表

一、需求

引出没有插件,只有引入有插件。

在引出时可能需要修改一些列头的样式风格,在引出时做一些对引出数据的修改。

我们需要一个更加自由地,自定义程度更高的方式去引出数据,并且在引出数据时,还能做到多个单据数据组合来引出。


在案例中,我并没有获取其他单据标识对应的单据的数据,但是理论上是可以实现:引出时,引出不同单据标识的数据。

只需要使用QueryServiceHelper查询其他单据标识,再按照上面的实现思路去导出数据。


单元格高度,单元格背景色,文字字体/颜色/字号 等等风格的定义,这些风格样式都可以通过XSSFWorkbook来实现,关于工作簿XSSFWorkbook类的一些具体用法,请各位开发者自行搜索。


二、思路与方案

实现思路是:

获取选择的列表数据,然后构造工作簿,使用工作簿生成输出流,再转成输入流,最后用调用文件服务,把输入流上传到文件服务器。

1. 创建java插件

2. 单据列表工具栏添加按钮,添加单据体列表插件

3. 编写java插件,分为几步:

    3.1 获取已选数据,构造查询字段,QueryServiceHelper查询获取数据

    3.2 构造XSSFWorkbook,构造行XSSFRow,构造单元格XSSFCell

    3.3 把选择的数据填入XSSFWorkbook里面的单元格XSSFCell

    3.4 构造OutputStream实例,把XSSFWorkbook写入OutputStream

    3.5 OutputStream转为InputStream

    3.6 创建文件路径,文件名,调用FileService的upload,把InputStream上传到文件服务器

    3.7 弹出下载链接弹框


三、实现过程

1.新建单据列表插件

image.png


2.单据列表设计器

注册插件

image.png

添加按钮

image.png


3.编写插件

3.1获取已选数据,构造查询字段,QueryServiceHelper查询获取数据

/**
 * @param entityName   单据标识
 * @param listColumns  列
 * @param selectedRows 已选行
 * @return
 */
private DynamicObjectCollection queryData(String entityName, List<IListColumn> listColumns, ListSelectedRowCollection selectedRows) {
    StringBuilder stringBuilder = new StringBuilder();
    // 构造查询字段
    for (int i = 0; i < listColumns.size(); i++) {
        IListColumn listColumn = listColumns.get(i);
        if (!listColumn.getListFieldKey().equals("fseq")) {
            if (i == listColumns.size() - 1) {
                stringBuilder.append(getFieldKey(entityName, listColumn));
            } else {
                stringBuilder.append(getFieldKey(entityName, listColumn) + ", ");
            }
        }
    }
    String selectFields = stringBuilder.toString();

    // 构造idList,获取已选行的id,之后作为过滤条件去查询数据
    List<Long> idList = new ArrayList<>();
    for (ListSelectedRow row : selectedRows) {
        idList.add((Long) row.getPrimaryKeyValue());
    }
    // QFilter的过滤条件为:单据id等于idList里面的这些id才符合获取条件,其他的数据不获取
    QFilter qFilter = new QFilter("id", QFilter.in, idList);
    // 查询数据
    DynamicObjectCollection dynamicObjectCollection =
            QueryServiceHelper.query(entityName, selectFields, qFilter.toArray());
                /*DynamicObject[] dynamicObjectArray =
                BusinessDataServiceHelper.load(entityName, selectFields, qFilter.toArray());*/
    return dynamicObjectCollection;
}


3.2构造XSSFWorkbook,构造行XSSFRow,构造单元格XSSFCell

合并第一行为标题

// 创建标题
XSSFRow headRow = sheet.createRow(0);
XSSFCell headCell = headRow.createCell(0);
headCell.setCellValue(title);
XSSFCellStyle setBorder = workbook.createCellStyle();
setBorder.setFillForegroundColor(new XSSFColor(Color.RED));// 设置背景色
headCell.setCellStyle(setBorder);
// 合并第1行的前几列,合并列数 = excel的列数
CellRangeAddress titleCellAddresses = new CellRangeAddress(0, 0, 0, excel.get(0).size() - 1);
sheet.addMergedRegion(titleCellAddresses);


构造XSSFCell

//写入数据
for (int i = 0; i < excel.size(); i++) {
    XSSFRow nrow = sheet.createRow(i + 1);
    for (int u = 0; u < excel.get(i).size(); u++) {
        XSSFCell ncell = nrow.createCell(u);
        ncell.setCellValue(excel.get(i).get(u));
    }
}



3.3把选择的数据填入XSSFWorkbook里面的单元格XSSFCell,设置风格样式

/**
 * excel生成
 * @param excel 数据源,List<List<String>>,外层的list是行,里面的List<String>是列,存储value
 *              excel.size()是行数,excel.get(n).size()是列数
 * @return
 */
private XSSFWorkbook excel(List<List<String>> excel, String title) {
    //创建excel工作簿
    XSSFWorkbook workbook = new XSSFWorkbook();
    //创建工作表sheet
    XSSFSheet sheet = workbook.createSheet();
    //设置默认列宽
    sheet.setDefaultColumnWidth(20);

    // 创建标题
    XSSFRow headRow = sheet.createRow(0);
    XSSFCell headCell = headRow.createCell(0);
    headCell.setCellValue(title);

    // 设置首行标题的一些风格样式
    XSSFCellStyle titleStyle = workbook.createCellStyle();
    // 设置背景色
    //设置填充方案
    titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
    // 正红色
    //setBorder.setFillForegroundColor(new XSSFColor(Color.RED));
    // 设置自定义填充颜色,天蓝色
    titleStyle.setFillForegroundColor(new XSSFColor(new Color(135,206,250)));
    // 设置水平居中
    titleStyle.setAlignment(HorizontalAlignment.CENTER);
    // 设置垂直居中
    titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
    // 设置字
    XSSFFont font = workbook.createFont();
    // 字体颜色橘红色
    font.setColor(new XSSFColor(new Color(255 ,69,0)));
    // 设置字号
    font.setFontHeight(20);
    // 设置字体
    font.setFontName("微软雅黑");
    titleStyle.setFont(font);

    headCell.setCellStyle(titleStyle);

    // 合并第1行的前几列,合并列数 = excel的列数
    CellRangeAddress titleCellAddresses = new CellRangeAddress(0, 0, 0, excel.get(0).size()-1);
    sheet.addMergedRegion(titleCellAddresses);

    // 单据列表数据风格样式,设置字体为黑体,字号15
    XSSFCellStyle billStyle = workbook.createCellStyle();
    XSSFFont billFont = workbook.createFont();
    billFont.setFontName("黑体");
    billFont.setFontHeight(15);
    billStyle.setFont(billFont);
    //写入单据列表数据
    for (int i = 0; i < excel.size(); i++) {
        // i+1是因为前面第1行加了一个标题,单据列表数据是从Excel的第2行开始的,所以要+1
        XSSFRow nrow = sheet.createRow(i+1);
        for (int u=0;u<excel.get(i).size();u++){
            XSSFCell ncell = nrow.createCell(u);
            ncell.setCellStyle(billStyle);
            ncell.setCellValue(excel.get(i).get(u));
        }
    }
    return workbook;
}


3.4构造OutputStream实例,把XSSFWorkbook写入OutputStream

OutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);


3.5OutputStream转为InputStream

public ByteArrayInputStream parse(final OutputStream out) throws Exception {
    ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
    final ByteArrayInputStream swapStream = new ByteArrayInputStream(baos.toByteArray());
    return swapStream;
}


3.6创建文件路径,文件名,调用FileService的upload,把InputStream上传到文件服务器

private String upload(String entityName, XSSFWorkbook workbook) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
    String fileName = entityName + sdf.format(new Date()) + ".xlsx";
    String pathName = "/offices/" + fileName;
    try {
        OutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        InputStream inputStream = parse(outputStream);

        FileService fs = FileServiceFactory.getAttachmentFileService();
        String path = fs.upload(new FileItem(fileName, pathName, inputStream));

        return path;
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
    return "";
}


3.7 弹出下载链接弹框

// 弹出提示框
getView().showMessage("下载文件链接:" + RequestContext.get().getClientFullContextPath() + "/attachment/download.do?path=" + path);


3.8 如果需要直接下载链接,可以用openUrl方法,在浏览器中打开新页签,访问下载链接。

这样浏览器就会直接下载这个excel文件。

getView().openUrl(RequestContext.get().getClientFullContextPath() + "/attachment/download.do?path=" + path)


四、效果图

image.png

image.png

image.png


打开Excel文件,效果如下,

image.png


五、开发环境版本

image.png


六、注意事项

异常场景

(1)在上传时,遇到了抛异常的场景,文件服务拒接连接。

可以从以下方向排查问题:

  • 文件服务的磁盘容量是否超出上限,尝试删除上传路径的部分文件,看看能否解决问题。

  • 文件服务是否在运行,尝试重启文件服务。

  • 服务端和客户端的端口号是否正确,默认是8100。

image.png


七、参考资料

【开发平台】指导手册

学习成长中心

插件开发


八、附件

附件包含补丁包和java源代码。

补丁包包含jar包和元数据包,请在mc中安装补丁包。

附件里的案例和本文的一些截图,在界面上略有差异,但实现方式是一致的。

插件代码: FruitOutPutListPlugIn.java,已上传至附件.


图标赞 25
25人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!