美文网首页
使用j2v8执行js,填充打印数据.实现后端动态配置打印模板

使用j2v8执行js,填充打印数据.实现后端动态配置打印模板

作者: _非_阳_ | 来源:发表于2019-01-25 16:58 被阅读4次

最近在参与公司的餐厅点餐系统开发,涉及到打印小票功能时,公司希望实现小票后端配置模板方式,最后采取如下方式实现

打印功能实现流程

目前获取打印信息是采用极光推送(这种方式其实有很多隐患)
用户下单-->极光-->主设备-->打印

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mVm = obtainViewModel(this, PrinterViewModel.class);
        mVm.getPrimaryPrinter();//1:进入主页,先拉取主设备id,判断当前设备是否是主设备
        observeData();
        JPushReceiver.NotificationHandler.execPendingTask();
        appUpdateController = new AppUpdateController(this);
        appUpdateController.checkUpdate();
        mMediaPlayer = MediaPlayer.create(BaseApplication.getInstance(), R.raw.new_order);
    }

    private void observeData() {
        mVm.loading.observe(this, aBoolean -> {
            if (aBoolean != null) {
                if (aBoolean)
                    showLoading("");
                else
                    hideLoading();
            }
        });
        mVm.isPrimaryDevice.observe(this, aBoolean -> {
            if (aBoolean != null) {
                if (aBoolean) {
                  //2:如果是主设备,再获取打印的模板信息,和打印机地址
                    mVm.getTemplateList();
                    mVm.getAllPrinters();
                }
            }
        });

        mVm.allPrintList.observe(this, printers -> {

            UserModelManager.getInstance().setPrinterInfo(printers);
            //auto connect when app start up
            if (checkNonNull(printers)) {
                //3:当拿到打印机时,如果打印机是未连接状态,先主动连接
                for (Printer printer : printers) {

                    if (printer.getType() == Constants.PRINTER_BLE) {
                        if (!printer.isConnected()) {
                            PrinterManager.getInstance().connectBle(printer.getSign(), printer.getName());
                        }
                    }
                    else if (printer.getType() == Constants.PRINTER_NET) {
                        DeviceConnFactoryManager deviceManager = PrinterManager.getInstance().getNetManager(printer.getSign());
                        if (deviceManager == null || !deviceManager.getConnState()) {
                            new DeviceConnFactoryManager.Build().setId(printer.getSign())
                                                                .setConnMethod(DeviceConnFactoryManager.CONN_METHOD.WIFI)
                                                                .setIp(printer.getSign())
                                                                .setPort(9100)
                                                                .setName("网络打印机")
                                                                .build();
                        }
                    }
                }
            } else {
                Log.e(TAG, "getAllPrinters  is empty ");
            }

        });

        mVm.template.observe(this, template -> {
            if (checkNonNull(template)) {
                UserModelManager.getInstance().setTemplateList(template.getTpl());
                //4:当拿到模板信息时,将模板的版本和sp中存的version进行对比
                int jsVersion = SharedPreferencesUtil.getInt(this, Constants.KEY_JS_VERSION, 0);
                //5:如果网络version大于本地的version,下载新模板(也就是js文件)
                if (template.getJs() != null && template.getJs().getVersion() > jsVersion) {
                    Data        data       = new Data.Builder().putString(Constants.KEY_JS_URL, template.getJs().getUrl()).build();
                    WorkRequest downloadJs = new OneTimeWorkRequest.Builder(DowloadJsWorker.class).setInputData(data).build();
                    WorkManager.getInstance().enqueue(downloadJs);
                    WorkManager.getInstance().getWorkInfoByIdLiveData(downloadJs.getId())
                               .observe(this, workInfo -> {
                    //6:js文件下载成功后,更新本地version号
                                   if (workInfo!=null&&workInfo.getState() == WorkInfo.State.SUCCEEDED) {
                                        SharedPreferencesUtil.putInt(this,Constants.KEY_JS_VERSION,template.getJs().getVersion());
                                   }
                               });
                }
            }
        });


    }

接下来就是实现打印操作

  1. 接收到推送时,判断是通知还是具体的打印数据

            String data = bundle.getString(JPushInterface.EXTRA_MESSAGE);//自定义消息中的推送内容
            try {
                NotificationInfo notificationInfo = new Gson().fromJson(data
                        , NotificationInfo.class);
                LogUtils.e("Notification data is :\n" + data);
                if (APP_NOTICE.equals(notificationInfo.channel)) {
                    NotificationHandler.notificationDatas.put(msgId, notificationInfo);
                    EventUtils.post(new NotificationEvent(msgId));
                } else if (PRINT_NOTICE.equals(notificationInfo.channel)) {
                    NotificationHandler.print(notificationInfo);
                }
            } catch (Exception e) {
                LogUtils.e(e);
            }

根据指示,判断打印什么单据

  public static void print(NotificationInfo notificationInfo) {
            JsonElement data = notificationInfo.msg.get("data");
            PrintData printData = new Gson().fromJson(data, PrintData.class);
            //并没有将打印数据解析成一个类,而是用String来接收打印数据,这样的好处是,无所谓后端如何更改打印数据的类型,前端不关心,直接丢给js解析成前端需要的格式就好了!
            String body = data.getAsJsonObject().get("body").toString();
            //supportRules是个int数据,1代表客看单,2 结账单,3制作单 可以动态配置打什么单据!
            JsonArray supportRules = notificationInfo.msg.get("supportRules").getAsJsonArray();


            for (JsonElement rule : supportRules) {
                PrintTools.PrintData pt=new PrintTools.PrintData();
                pt.rule=rule.getAsInt();
                pt.printer_id=printData.printer_id;
                pt.body=body;
                pt.store_name=printData.store_name;
                pt.u_t=printData.u_t;
          //打印操作
                PrintTools.print(pt);
            }
        }
    }
  
    private class NotificationInfo {
        private String channel;
        private String title;
      
        private Map<String, JsonElement> msg;
    }

打印操作

 public static void print(@NonNull PrintData printData) {
        Observable.create((ObservableOnSubscribe<String>) emitter -> {
            //1:读取本地js文件
            String jsContent = getFileContent(Constants.JS_FILE_PATH);
            if (TextUtils.isEmpty(jsContent)) {
                emitter.onError(new Exception("读取js文件出错或Js文件内容为空!"));
            }
            emitter.onNext(jsContent);
            emitter.onComplete();
        })
                  .flatMap((Function<String, ObservableSource<Wrapper>>) jsContent -> {
                    //2:读取成功后,获取打印机信息
                      Printer printer = UserModelManager.getInstance().getPrinter(printData.printer_id);
                      if (printer != null) {
                  //使用J2V8引擎来运行js
                          V8 v8 = V8.createV8Runtime();
                          v8.executeScript(jsContent);
                  //3:给js提供android日志回调,方便发现问题
                          v8.registerJavaMethod(new JavaCallback(), "log", "log", new Class<?>[]{String.class});

                          int paperWidth = UserModelManager.getInstance().getPrinter(printData.printer_id).getPaper_width();
                          //设置一行打印的字节数
                          JSONObject jo = new JSONObject();
                          if (paperWidth == 58) {
                              jo.put("pageWidth", LINE_BYTE_SIZE_58);
                          } else {
                              jo.put("pageWidth", LINE_BYTE_SIZE_88);
                          }
                          //打印数据
                          String data = printData.body;

                          //模板
                          String                               template     = "";
                          List<PrintTemplateBean.TemplateBean> templateList = UserModelManager.getInstance().getTemplateList();
                          if (!checkNonNull(templateList)) {
                              Log.e(TAG, "打印模板不存在!");
                          } else {
                              for (PrintTemplateBean.TemplateBean bean : templateList) {
                                  if (bean.getType() == printData.rule) {
                                      template = bean.getLayouts();
                                      break;
                                  }
                              }
                          }

                          if (!checkNonNull(template)) {
                              Log.e(TAG, "template error: templateData is empty");
                          }
                          //4:得到js解析后的打印数据
                          String printStr = (String) v8.executeJSFunction("run", jo.toString(), data, template);
                          Log.e(TAG, "print: \n" + printStr);
                          v8.release();
                          //5:将打印数据解析成PrintLineData数组,一个PrintLineData就是一行!
                          List<PrintLineData> datas   = GsonUtils.gsonToList(printStr, PrintLineData.class);
                          Wrapper             wrapper = new Wrapper();
                          wrapper.printer = printer;
                          wrapper.data = datas;
                          return Observable.just(wrapper);
                      } else {
                          Log.e(TAG, "未找到 id为" + printData.printer_id + "的打印机!");
                      }
                      return Observable.just(new Wrapper());
                  })
                  .subscribeOn(Schedulers.io())
                  .subscribe(wrapper -> {
                      if (checkNonNull(wrapper.data)) {
                          //6:开始连接打印机,进行打印
                          print(wrapper);
                      }
                  }, throwable -> Log.e(TAG, "print error ", throwable));
    }

    //打印!!!
    private static void print(Wrapper wrapper) {
        EscCommand esc = new EscCommand();
        esc.addInitializePrinter();

        for (PrintLineData line : wrapper.data) {
            if (checkNonNull(line.getColumns())) {
                if (line.getColumns().size() > 1) {//多列
                    for (PrintLineData.ColumnsBean column : line.getColumns()) {
                        //加粗
                        boolean isBold = column.getBold() == 1;
                        esc.addSelectPrintModes(EscCommand.FONT.FONTA, isBold ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);
                        //倍高倍宽
                        boolean isLarge = column.getFontSize().equals("large");
                        esc.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);

                        esc.addSelectJustification(column.getTextAlignment());
                        esc.addText(column.getContent());
                    }
                    esc.addText("\n");
                } else {
                    PrintLineData.ColumnsBean column = line.getColumns().get(0);
                    //加粗
                    boolean isBold = column.getBold() == 1;
                    esc.addSelectPrintModes(EscCommand.FONT.FONTA, isBold ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);
                    //倍高倍宽
                    boolean isLarge = column.getFontSize().equals("large");
                    esc.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);
                    esc.addSelectJustification(column.getTextAlignment());
                    esc.addText(column.getContent() + "\n");
                }
            }
        }
        esc.addPrintAndFeedLines((byte) 1);
        esc.addCutPaper();

        Vector<Byte> datas = esc.getCommand();
        // 发送数据
        DeviceConnFactoryManager manager = PrinterManager.getInstance().getManager(wrapper.printer);
        if (manager != null) {
            manager.openPort();
            manager.sendDataImmediately(datas);
        }
    }

    public static class JavaCallback {
        public void log(String error) {
            Log.e(TAG, "print lllllll ," + error);
        }
    }

    public static class PrintData {
        public String printer_id;

        public String store_name;

        public String operator_name;

        public int rule;

        public String body;

        /**
         * 打印时间:【2018-09-23 17:55:09】
         */
        public String u_t;
    }

这是最开始的打印功能实现,还有很多bug和问题,之后会慢慢记录出来!

相关文章

  • 使用j2v8执行js,填充打印数据.实现后端动态配置打印模板

    最近在参与公司的餐厅点餐系统开发,涉及到打印小票功能时,公司希望实现小票后端配置模板方式,最后采取如下方式实现 目...

  • C# 修改打印机名称

    模板指定打印机名称,可以修改默认打印机名称匹配模板打印机,实现自动打印。否则需要手动配置打印机。 引用组件: us...

  • U9集成条码打印机(科诚)

    1. 配置打印模板 【基础设置(集团)】-【打印配置】 实体:需要打印的实体数据类型:常量、字段(取值来自实体)字...

  • VUE使用富文本自定义打印模板

    当面对千千万万的打印格式需求时,这时可能需要使用打印模板。但是后端使用客户提供的模板,往往需要进行“二次加工”。所...

  • 四、实现“动态组装”打印机

    一、本课目标 实现“动态组装”的打印机 二、依赖注入综合实例 2.1如何开发一个打印机? 可灵活配置使用彩色墨盒或...

  • 【硬派网络计费系统】-  票据模板管理

    系统使用了第三方商业打印控件实现票据打印,根据打印控件的规范,需要设计符合要求的票据模板,系统支持在线设计模板。 ...

  • 打印echarts的图表

    echarts的图表是动态的数据,在使用web端打印的时候,会将打印的图表数据丢失。 解决上述问题的方法就是将动态...

  • vue实现打印功能

    打印功能可以自己使用原生js(window.print())实现,其次就是使用插件,介绍一个打印插件 vue-pr...

  • 前端实现打印功能(筛选打印表格)

    前端实现打印也并没有那么难。起初是后端实现打印,前端调取接口,但无法实现单选框的样式,无法对数据进行筛选,这才选择...

  • Web工具

    1.ireport可配置打印模板

网友评论

      本文标题:使用j2v8执行js,填充打印数据.实现后端动态配置打印模板

      本文链接:https://www.haomeiwen.com/subject/hhfvjqtx.html