dexteryao 发表于 2020-8-31 14:52:58

SpreadJS 全家桶实践 - 在线Excel报表系统(二)

本帖最后由 dexteryao 于 2020-8-31 14:52 编辑

上一期SpreadJS 全家桶实践 - 在线Excel报表系统(一),已经搭建好了整个项目框架并集成了SpreadJS、新编辑器和GCExcel。
本期将实现模板上传加载功能,并通过GCExcel的模板功能实现数据绑定。

1. 自定义Ribbon(工具栏),添加上传、加载、绑定和模板按钮
<blockquote><script src="lib/pako.min.js" type="text/javascript"></script>将编辑器默认配置default_config.json修改为designerConfig.js 并引入

let designerConfig = {
    "ribbon":[ ... ]添加designer.js和ribbon.file.js分离功能,方便维护
designer.js中添加自定义config,在初始化编辑器时使用自定义config
var customerRibbon = {
    "id": "operate",
    "text": "操作",
    "buttonGroups": [
    ]
}

function initStates(designer) {
    spreadTemplateJSON = undefined;
    designer.setData(AllowBindingData, true)
    designer.setData(AllowBackTemplate, false)
}

function initDesigner() {

    customerRibbon.buttonGroups.push(ribbonFileConfig) //将ribbon.file config注入
    designerConfig.ribbon.push(customerRibbon) //添加自定义ribbon tab

    var designer = new GC.Spread.Sheets.Designer.Designer(
      document.getElementById('ssDesigner'), /**designer host */
      designerConfig, // designerConfigJson /**If you want to change Designer's layout or rewrite it, you can pass in your own Config JSON */ ,
      undefined /**If you want to use the spread you already created instead of generating a new spread, pass in */
    );
    initStates(designer);
    return designer
}修改index.js,初始化调用initDesigner 方法,并引入pako,添加前端压缩解压方法
var designer
window.onload = function () {
    GC.Spread.Common.CultureManager.culture("zh-cn");
    designer = initDesigner();
};

在ribbon.file.js添加 ribbonFileConfig ,设置上传、加载、绑定和模板按钮,并指定icon和命令
var ribbonFileConfig =
{
    "label": "文件操作",
    "thumbnailClass": "ribbon-thumbnail-spreadsettings",
    "commandGroup": {
      "children": [
            {
                "direction": "vertical",
                "commands": [
                  {
                        iconClass: "ribbon-button-download",
                        text: "加载",
                        commandName: "getTemplates",
                        execute: getTemplates,
                  },
                  {
                        iconClass: "ribbon-button-upload",
                        text: "上传",
                        commandName: "uploadFile",
                        execute: uploadFile,
                  }
                ]
            },

            {
                "type": "separator"
            },
            {
                "direction": "vertical",
                "commands": [
                  {
                        iconClass: "ribbon-button-namemanager",
                        text: "绑定",
                        commandName: "bindingData",
                        execute: bindingData,
                        enableContext: AllowBindingData
                  },
                  {
                        iconClass: "ribbon-button-namemanager",
                        text: "模板",
                        commandName: "backTemplate",
                        execute: backTemplate,
                        enableContext: "AllowBackTemplate"
                  }
                ]
            }
      ]
    }
};添加命令对应方法
getTemplates 通过接口获取服务器上模板文件,并在表单中通过超链接形式展示
点击超链接后调用loadTemplate,通过模板名称加载模板json,导入工作簿
uploadFile 方法触发文件选择器,选择文件通过接口上传,服务端GCExcel 处理后返回json,并加载。可上传.xlsx Excel文件和SpreadJS ssjson
bindingData 将模板提交GCExcel与模拟数据绑定并返回结果json
backTemplate 通过缓存的json,还原回模板状态



async function getTemplates(designer) {
    var spread = designer.getWorkbook();

    $.post("spread/getTemplates", {}, function (data) {
      if (data != "0") {
            data = ungzipString(data);
            console.log(data)
            var files = data.split(";")
            var sheet = spread.getActiveSheet();
            sheet.reset();
            sheet.defaults.colWidth = 180
            sheet.setArray(0, 0, )
            // var HyperLinkCellType = GC.Spread.Sheets      
            var h = new GC.Spread.Sheets.CellTypes.HyperLink();
            h.onClickAction(function (e) {
                var sheet = e.sheet, row = e.row, col = e.col;
                var value = sheet.getValue(row, col);
                loadTemplate(value)
            })
            sheet.getRange(0, 0, 1, files.length).cellType(h)
      }
    })
}
async function loadTemplate(fileName) {
    var spread = designer.getWorkbook();

    let formData = new FormData();
    formData.append("fileName", fileName);

    $.ajax({
      url: "spread/loadTemplate",
      type: "POST",
      contentType: false,
      processData: false,
      data: formData,
      success: function (data) {
            if (data != "0") {
                data = ungzipString(data);
                var json = JSON.parse(data);
                json.calcOnDemand = true;
                spread.fromJSON(json, { doNotRecalculateAfterLoad: true });
                initStates(designer)
            }
      }
    })
}
async function uploadFile(designer) {
    var spread = designer.getWorkbook();
    var ribbon_file_selector = document.getElementById("ribbon_file_selector");
    ribbon_file_selector.onchange = function (event) {
      console.log(event)
      var file = event.target.files;
      let formData = new FormData();
      formData.append("file", file);
      formData.append("fileName", file.name);

      $.ajax({
            url: "spread/uploadFile",
            type: "POST",
            contentType: false,
            processData: false,
            data: formData,
            success: function (data) {
                if (data != "上传失败!") {
                  data = ungzipString(data);
                  var json = JSON.parse(data);
                  json.calcOnDemand = true;
                  spread.fromJSON(json, { doNotRecalculateAfterLoad: true });

                  initStates(designer)

                }
                else {
                  alert("上传失败")
                }
            }
      })
    }
    ribbon_file_selector.value = "";
    ribbon_file_selector.click();

}


var spreadTemplateJSON;
var AllowBindingData = "AllowBindingData", AllowBackTemplate = "AllowBackTemplate";
async function bindingData(designer) {
    var spread = designer.getWorkbook();
    if (!spreadTemplateJSON) {
      spreadTemplateJSON = JSON.stringify(spread.toJSON())
    }
    var uploadData = gzipString(spreadTemplateJSON);
    $.post("spread/bindingData", { data: uploadData }, function (data) {
      if (data != "0") {
            data = ungzipString(data);
            var json = JSON.parse(data);
            json.calcOnDemand = true;
            spread.fromJSON(json, { doNotRecalculateAfterLoad: true });
      }
    })
    designer.setData(AllowBackTemplate, true)
}
async function backTemplate(designer) {
    if (spreadTemplateJSON) {
      var spread = designer.getWorkbook();
      spread.fromJSON(JSON.parse(spreadTemplateJSON))
      spreadTemplateJSON = undefined;
    }
    designer.setData(AllowBindingData, true)
    designer.setData(AllowBackTemplate, false)
}
2. 添加服务支持
引入GCExcel ,pom.xml加入
<!-- https://mvnrepository.com/artifact/com.grapecity.documents/gcexcel -->
                <dependency>
                        <groupId>com.grapecity.documents</groupId>
                        <artifactId>gcexcel</artifactId>
                        <version>3.2.2</version>
                </dependency>新建SpreadController,提供api接口
@RestController
@RequestMapping({"Spread","spread"})
public class SpreadController {

    public SpreadController() {
//      指定字体文件路径
//      Workbook.FontsFolderPath = "/Users/dexteryao/Documents/Projects/IdeaProjects/common/pdfFont";
    }
getTemplates获取resources/reports中模板文件
/*获取reports路径下模板文件列表,返回分号分割文件名string*/
    @RequestMapping(value="/getTemplates",method= RequestMethod.POST)
    public String getTemplates(){
      try {
            var path = "src/main/resources/reports";
            File reportDir = ResourceUtils.getFile(path);
            File[] files = reportDir.listFiles();
            String result = "";
            for(int i=0;i<files.length;i++){
                File file = files;
                if(file.isFile() && !file.isHidden()) {
                  result += (file.getName() + ";");
                  System.out.println("^^^^^" + file.getName());
                }
            }
             result = compress(result);
            return result;
      } catch (Exception e) {
            e.printStackTrace();
      }
      return "0";
    }
loadTemplate加载模板文件,Excel文件通过GCExcel转JSON
    /*通过文件名,返回压缩后json,如果是Excel文件,使用GCExcel toJSON*/
    @RequestMapping(value="/loadTemplate",method= RequestMethod.POST)
    public String loadTemplate(@RequestParam("fileName") String fileName){
      try {
            var filePath = "src/main/resources/reports/" + fileName;
            String workbookJSON;
            Workbook workbook = new Workbook();

            System.out.println("开始获取数据:" + new Date());
            if(fileName.endsWith("xlsx")) {
                URL url = ResourceUtils.getURL(filePath);
                workbook.open(url.getFile());
                workbookJSON = workbook.toJson();
            }
            else{
                workbookJSON = getTextFileContent(filePath);
            }

            System.out.println("开始压缩:" + new Date());
            String result = compress(workbookJSON);

            System.out.println("压缩结束:" + new Date());
            return result;
      } catch (Exception e) {
            e.printStackTrace();
      }
      return "0";
    }

uploadFile上传模板,Excel通过GCExcel 预处理后返回json。此处可直接保持JSON
/*上传文件,报错到reports文件夹,excel和ssjson都通过GCExcel 处理一遍后再返回压缩后ssjson,用于测试GCExcel */
    @RequestMapping(value="/uploadFile", headers = ("content-type=multipart/form-data"), method=RequestMethod.POST)
    public String uploadFile(@RequestParam("file") MultipartFile file,
                            @RequestParam("fileName") String fileName) throws FileNotFoundException {
      if (file.isEmpty()) {
            System.out.println("文件空");
            return "上传失败!";
      }

      String filePath = "src/main/resources/reports/" + fileName;
      URL url = ResourceUtils.getURL(filePath);
      System.out.println(filePath);
      File dest = new File(url.getFile());
      try {
            file.transferTo(dest);

            Workbook workbook = new Workbook();

            if(fileName.endsWith("xlsx")) {
                workbook.open(url.getFile());
            }
            else{
                String workbookJSON = getTextFileContent(url.getFile());
                workbook.fromJson(workbookJSON);
            }
            String workbookJSON = workbook.toJson();
            String result = compress(workbookJSON);

            return result;
      } catch (IOException e) {
            e.printStackTrace();
      }
      return "上传失败!";
    }bindingData绑定数据,数据为模拟数据,设计模板时候需按照数据模型设计器
    /*绑定模拟数据*/
    @RequestMapping(value="/bindingData",method= RequestMethod.POST)
    public String BindingData(@RequestParam(value = "data", required = true) String data){
      try {
            String json = uncompress(data);
            if(json != null && !json.equals("")) {

                Workbook workbook = new Workbook();
                workbook.setEnableCalculation(false);
                workbook.fromJson(json);

                bindingDataToWrokbook(workbook);

                System.out.println("开始toJSON:" + new Date());
                String workbookJSON = workbook.toJson();
                System.out.println("开始压缩:" + new Date());
                String result = compress(workbookJSON);
                System.out.println("结束:" + new Date());
                return result;
            }
            return "0";
      } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
      }
      return "0";
    }数据模型
public class DataSourceModel {
    public String SecondaryInstitution;
    public String EndDate;
    public String EndMonth;
    public int InsuranceYear;
    public String Area;
    public int InsuranceCount;
    public int BaseAmount;
}
模拟数据并绑定到workbook
public voidbindingDataToWrokbook(Workbook workbook){

      System.out.println("开始创建模拟数据:" + new Date());
      List<DataSourceModel> ds1 = new ArrayList<>() ;
      for(int i = 0; i< 30; i++){
            for(int j = 0; j < 77; j++) {
                for(int k = 0; k < 2; k++) {
                  DataSourceModel dsm = new DataSourceModel();
                  dsm.Area = "地区" + j;
                  dsm.BaseAmount = i * j * 6 + k;
                  dsm.InsuranceYear = 2020;
                  dsm.SecondaryInstitution = "机构" + j + k;
                  dsm.InsuranceCount = (j % 5) * i + k;
                  dsm.EndMonth = "2020-" + (k % 2 + 8);
                  dsm.EndDate = dsm.EndMonth + "-" + (i + 1);
                  ds1.add(dsm);
                }
            }
      }
      System.out.println("模拟数据行数:" + ds1.size());
      System.out.println("开始添加数据:" + new Date());
      workbook.addDataSource("ds", ds1);
      System.out.println("开始绑定数据:" + new Date());
      workbook.processTemplate();
      System.out.println("结束绑定数据:" + new Date());
    }

GZip压缩解压方法,获取文本文件内容
public static String compress(String str) {
      if (str.length() <= 0) {
            return str;
      }
      try{
            ByteArrayOutputStream bos = null;
            GZIPOutputStream os=null; //使用默认缓冲区大小创建新的输出流
            byte[] bs =null;
            try{
                bos = new ByteArrayOutputStream();
                os = new GZIPOutputStream(bos);
                os.write(str.getBytes()); //写入输出流
                os.close();
                bos.close();
                bs = bos.toByteArray();
                return new String(bs, "ISO-8859-1");
            }finally{
                bs = null;
                bos = null;
                os = null;
            }
      }catch(Exception ex){
            return str;
      }
    }

    public static String uncompress(String str) {
      if (str.length() <= 0) {
            return str;
      }
      try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes("ISO-8859-1"));
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte;
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
            return new String(out.toByteArray(), "UTF-8");
      } catch (Exception e) {

      }
      return str;
    }

    private String getTextFileContent(String path){
      try {
            File file = ResourceUtils.getFile(path);
            String encoding = "UTF-8";
            Long fileLength = file.length();
            byte[] fileContent = new byte;
            FileInputStream in = new FileInputStream(file);
            in.read(fileContent);
            in.close();
            String content = new String(fileContent, encoding);
            return content;
      }
      catch (Exception e){

      }
      return "";
    }


至此,上传加载,数据绑定功能也已实现





页: [1]
查看完整版本: SpreadJS 全家桶实践 - 在线Excel报表系统(二)