不简说

V1

2023/04/02阅读:21主题:自定义主题1

【vue-plugin-hiprint】实战-动态provider

哈喽~ 各位码友😄小伙伴们😊在使用此插件的时候,应该都有这样的需求,那就是动态加载配置的打印元素。所以~~安排实战篇-动态provider

本次实战源码链接: https://github.com/CcSimple/vue-plugin-hiprint-start

效果如图:

动态provider效果图
动态provider效果图

前言

本篇围绕以下几点进行阐述:

  1. provider 对象格式
  2. 分析 addElementTypes 方法
  3. 封装provider helper工具 本篇重点
  4. 动态构建 provider
  5. 动态更新 provider

1.provider对象格式

虽然我在【vue-plugin-hiprint】如何自定义可拖拽元素 provider一文中已经阐述过了,这里还是对各位小伙伴简单说一下,直接看下方代码(注释还算够细可以吧😊):

/**
 * 我在 【vue-plugin-hiprint】如何自定义可拖拽元素 provider 一文中, 已经详细介绍了
 * 链接: https://mp.weixin.qq.com/s/n9i1j8hhVJvnlfJRPRtWog
 * 格式如下:
 * @param {Object} options 这是我们实际传入的参数, 一般是一个对象, 里面包含了一些配置信息
 * @return {Object} 返回一个对象, 里面包含了 addElementTypes 方法
 */

// eslint-disable-next-line no-unused-vars
const 格式 = function (options{
  // 这里的 options 是一个对象, 是我们在使用时传入的
  // 当然你可以自定义有几个参数 如: function (map,options) {}
  console.log(options);
  // 这里的 context 是一个对象, 是由 hiprint 内部执行时回调回来的.
  // 我们可以 log 出来看看
  var addElementTypes = function (context{
    console.log(context);
  };
  return {
    addElementTypes: addElementTypes,
  };
};

简单来说,就是返回一个包含 addElementTypes方法的对象。


2.分析addElementTypes方法

其实也就是去 封装实现 addElementTypes 方法,然后返回。方便在构建之前init 初始化。

在封装之前我们肯定需要清楚这个 addElementTypes 方法到底做了些什么吧,我们先来分析一下默认的provider(defaultElementTypeProvider);如下图:

addElementTypes分析
addElementTypes分析

如上图,那么就可以有个大致思路,那就是需要:

  • key(用于区分和构建使用),
  • groupName(就是个分组名称),
  • printElements(实际元素参数对象,与后端协商确定)

我的思路是这样的:

const createProvider = function (key, options{
  const addElementTypes = function (context{
      // 先清空, 避免重复添加. 如果有特殊需求, 可以不清空
      context.removePrintElementTypes(key);
      // 实际添加 一般分为以下几步:
      // 1. 添加一个key (用于创建 "唯一" 的 "tid" ) -- 对象包含数组: {"key",[]}
      // 2. 添加一个分组 (就是为了给元素分组, 便于展示) -- 对象包含数组: {"分组名称",[]}
      // 3. 添加分组下的元素数组 (实际的元素操作,都在这里进行) -- 数组包含对象 [{元素格式},{元素格式}]
  }
}

👌🏻,大致思路有了,那就撸起袖子编码呗~ 有问题再调整咯,不然还有什么办法呢🤦。


3.封装provider-helper

新建一个 provder-helper.js 文件;根据先前的分析先撸两个 方法 export 一下咯~:

/**
 * 创建一个 provider, 我这里示例,传入两个参数
 * @param {*} key 这个 key 是用于创建 "唯一" 的 "tid" 的, 一般是用于区分不同的 provider
 * @param {*} options 这个就需要根据实际情况来定义了,根据项目实际情况与后端协商定义
 */

const createProvider = function (key, options{
  const addElementTypes = function (context{
    // todo: 待实际实现的方法
  }
  return {
    addElementTypes: addElementTypes,
  };
}
/**
 * 创建多个 provider
 * @param {*} optionList 参数列表
 */

const createProviderList = function (optionList{
  const providers = optionList.map((item) => {
    return createProvider(item.key, item.options);
  });
  // 当你不清楚的时候, 可以 log 出来看看
  return providers;
};
export default { createProvider, createProviderList };

嗯~ 还算清晰😄;现在就需要各位小伙伴自己挠一挠动一动脑子了,根据打印元素的数据格式,怎么去实现这个 addElementTypes 方法。

我先打个样:

  • 假设后端返回的数据如下:
const data = {
  key: "NetProvider1",
  options: {
    groupName: "NetProvider1分组名称",
    printElements: [
      {
        id: "name",
        type"txt"// 这里的 type 是后端返回的, 需要转换成 hiprint 的 type
        width: 100,
        height: 20,
        title: "名称",
        field: "name",
        testData: "名称1",
        // 协商的可选参数, 没有可选参数就返回 {}
        options: {
          fontSize: 12,
          color: "#f00808",
        },
      },
      {
        id: "logo",
        type"img",
        width: 100,
        height: 100,
        title: "logo",
        field: "logo",
        options: {
          src: "https://foruda.gitee.com/avatar/1677050350324030848/5400665_ccsimple_1591166830.png!avatar200",
        },
      },
      {
        id: "table",
        type"table",
        title: "空白表格",
        field: "table",
        options: {},
      },
      {
        id: "tableCustom",
        type"tableCustom",
        title: "默认表格",
        field: "table",
        options: {},
        columns: [
          [
            // 省略
          ],
        ],
      },
    ],
  },
};
  • 如上数据格式后端返回元素类型(type)与我们实际需要不一致。那么我们就需要去转换一下了:
/**
 * @description 这是一个示例, 如果后端返回的数据,与我们需要的格式不对应, 那么我们可以在这里进行转换
 */

const PRINT_ELEMENT_TYPE_MAP = {
  // 比如后端返回元素类型的是 "txt", 但是我们需要的是 "text"
  txt: "text",
  img: "image",
  // 比如后端返回的 "二维码", 但是我们需要的是 "text", 还需要特殊处理
  qrcode: "text",
  // 比如后端返回的 "条形码", 但是我们需要的是 "text", 也需要特殊处理
  barcode: "text",
  table: "table",
  tableCustom: "table",
  hLine: "hline"// 后端返回的它就是不一样,怼他
  vLine: "vline",
  rect: "rect",
  oval: "oval",
};

有了这个假设,那么就继续干代码~

前面分析到,需要 1、2、3个步骤,但是呢,这个 1 需要 2步的内容2步 需要 3步的内容。嗯~ 所以说,倒过来处理呗:

反过来说, 我们就是需要:

  • 先创建元素数组
  • 再把这个"元素数组"push到 "分组对象"中
  • 最后把这个"分组对象"push到 "key的数组"中

👌上代码:

const createProvider = function (key, options{
  const addElementTypes = function (context{
    // 先清空, 避免重复添加. 如果有特殊需求, 可以不清空
    context.removePrintElementTypes(key);
    // 第 3 步: 创建元素数组
    let printElements = options.printElements.map((item) => {
      // 提出来, 方便处理 二维码 条形码;
      // 如果和后端约定好,那么就更简单了,直接把后端返回的数据,直接赋值给 options
      let options = {
        title: item.title, // 在 options 中添加 title, 是可以清空的
        field: item.field,
        testData: item.testData,
        ...item.options, // 可选参数之类的, 或者参数都在这里面
      };
      // 有些元素可以不设置宽高的,比如 table
      item.width && (options.width = item.width);
      item.height && (options.height = item.height);
      // 特殊处理 二维码 条形码
      if (["qrcode""barcode"].includes(item.type)) {
        options.textType = item.type;
      }
      // 基础打印元素选项
      let printElement = {
        tid: `${key}.${item.id}`// 确保不重复就行
        title: item.title, // 这个 title 清空了,还是会有这个默认值
        type: PRINT_ELEMENT_TYPE_MAP[item.type], // 转换后端返回的元素类型
        options: options,
      };
      // 特殊处理 表格 (表格参比较多咯~~~)
      if (["table""tableCustom"].includes(item.type)) {
        // 根据实际情况 进行处理
        if (item.columns) {
          printElement.columns = item.columns;
        } else {
          printElement.columns = [
            [
              { align: "center", width: 100 },
              { align: "center", width: 100 },
            ],
          ];
        }
        return printElement;
      }
      return printElement;
    });
    // 第 2 步: 创建分组对象
    let printElementGroup = new hiprint.PrintElementTypeGroup(options.groupName, printElements);
    // 第 1 步: 添加到 key 数组中
    context.addPrintElementTypes(key, [printElementGroup]);
  };
  return {
    addElementTypes: addElementTypes,
  };
};

就是这样几步完成了这个封装;当然如果后续有什么调整调整这个工具类就好了。


动态构建 provider

工具封装好了,那就上手试一试有报错就看报错原因如果没有,那么恭喜你,我看你骨骼惊奇,是个奇才,非常适合这本.... 思路非常清晰~

// 模拟网络请求,2秒后返回数据。
// clear: 是否需要先清空 再构建
const getProvider1 = (clear = true) => {
  new Promise((resolve, reject) => {
    loading.value = true;
    setTimeout(() => {
      resolve(netData1);
    }, 2000);
  }).then((data) => {
    buildProvider(data, clear);
  });
};
/**
 * 构建provider
 * 注意: 构建可拖拽元素必须在 hiprint.init() 之后调用
 * 调用之前 可以先 console.log($("#provider-container")) 看看是否有该 dom
 */

const buildProvider = (data, clear) => {
  // 组装 provider
  if (Array.isArray(data)) { // 如果返回的是数组
    let providerList = helper.createProviderList(data);
    hiprint.init({ providers: providerList });
  } else {
    let provider = helper.createProvider(data.key, data.options);
    hiprint.init({ providers: [provider] });
  }
  if (clear) {
    $("#provider-container").empty(); // 先清空, 避免重复构建
  }
  if (Array.isArray(data)) { // 数组就循环去构建
    data.forEach((item) => hiprint.PrintElementTypeManager.build($("#provider-container"), item.key));
  } else {
    hiprint.PrintElementTypeManager.build($("#provider-container"), data.key);
  }
  loading.value = false;
};

实际效果,各位小伙伴可在线上demo去体验


动态更新 provider

这个其他就是通过hiprint.updateElementType这个 API 去动态更新已初始化(init)provider。 只是很多小伙伴可能不知道这个🤦...

  • 比如有个需求说,这个新拖拽的元素,根据当前登录的用户名来生成。

那么此时,就可以使用这个来动态更新provider

/**
 * @param String tid (provider中唯一的值)
 * @param {Function} 回调这个tid的对象
 * @return {Object} 返回修改后的对象
 */

hiprint.updateElementType('defaultModule.text'(type) => {
  // 不知道格式的情况,就 log 看看
  console.log(type);
  type.title = '这是更新后的元素';
  return type;
})

就是这个简单😊。


总结

  • 整篇下来,可以看到,实际写代码之前分析是很重要的;
  • 当然与后端配合的时候,相互协商也是很有必要
  • 不理解对象格式的时候,一定要去观察分析结构/格式
  • 代码封装,平时写的时候能想到的还是封装一下

参数篇到此结束,如有不清楚知识点,大家一定要学会自己查阅相关资料

如果还想熟悉/了解 【vue-plugin-hiprint】其他相关功能,欢迎各位留言探讨

觉得不错就点个赞再走咯~

分类:

前端

标签:

前端

作者介绍

不简说
V1