Giter Club home page Giter Club logo

xinglie / report-designer Goto Github PK

View Code? Open in Web Editor NEW
840.0 840.0 224.0 56.86 MB

⚡打印设计、可视化、标签打印、编辑器、设计器、数据分析、报表设计、组件化、表单设计、h5页面、调查问卷、pdf生成、流程图、试卷、SVG、图形元素、物联网、标签纸

Home Page: https://xinglie.github.io/report-designer/

HTML 99.60% Shell 0.40%
cloud-print data-analysis data-visualization editor h5-creator h5-editor h5-maker iot-demo layouts-and-renderings online-design online-printing printer snapshot visiual-editor xinglie

report-designer's Introduction

Concact me 👋

  • 微信ID:qq84685009

GitHub Status Github Languages

report-designer's People

Contributors

xinglie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

report-designer's Issues

设计器技术方案

分而治之的思路在各行各业都可以看到,设计器里也不例外。

在设计器里所有的一切都是分而独立的,比如头部元素栏、工具栏、标尺等。大到面板,小到可被设计的元素均是互相隔离且独立的,彼此之间不知道对方存在。理论上设计器中只要被拆分的界面均可以渲染到其它页面的任意位置上,这为调整软件界面布局提供了很大的便利。

分的思路也为我们插件化的工程打下了良好的基础,当某些功能不需要或添加新功能时,只需要简单的删除或增加相应的代码即可。在设计器中,所有的一切均为插件化,包括基础的工具栏、标尺、右键菜单等,这些仅是设计器中数据状态的一个展示层,不与设计器核心代码偶合

设计器中的核心模块也是采用分的思路进行开发,历史记录、剪切板、元素操作、吸附对齐、选区、快捷键等均为互相独立的模块,这些模块所有的操作都是控制内存中的数据描述,即使没有界面一样可以完成数据的修改变换。当然,界面的存在仅是对这份数据描述进行可视化展示,实时反映内存中数据的状态和对应的可操作项,便于向用户反馈和方便数据修改。

大的组织还是需要独立的单元互相合作来完成更复杂的事情

项目中所有父到子的传递均由参数直接控制,即子提供控制参数,在父级进行传参控制。所有子到父的传递均是标准的DOM事件派发,这样更利于与第三方进行合作,只需要把设计器中的元素当成标准的HTML标签即可,所有事件均可以通过标准的addEventListenerremoveEventListener进行添加和移除

所有需要联动的元素均由中介者进行控制,元素各自仍是互相独立的,通讯控制则交与中介进行管理,这样在不引入中介时,彼此不会联动,引入中介时,由中介控制它们进行联动。中介模块也是插件化的存在。

性能的关键点

编译

基于magix的项目背后有自己的打包工具:https://github.com/thx/magix-composer

该工具不仅做代码编译,同时也检测项目中样式:未使用的、重名的、不推荐的写法等问题。检测项目中模板:使用未声明的选择器、未声明的变量、参数传递等问题。

在编译转换的同时,保证代码质量,确保每个人都写出合规的代码。

在做模板转换时,工具会尽可能的检测出模板中哪些是动态的,哪些是静态的,哪些是一次生成可反复使用的,一个变量变化引起哪些模板变化等各种信息,用于运行时最小化的改变界面。

在我写过的文章中可以查看:局部刷新与模板
模板编译与优化

简单来讲:通过离线的编译,使用工具提前优化好相关的代码,减少运行时不必要的处理,因为性能是从点滴做起的。

运行

编译后的代码终归是要在浏览器里结合最新数据来渲染出界面供用户使用的。

模板的处理

在我以往的文章中,谈过运行时的界面更新,magix中的界面异步更新
以及在这个仓库的边界说明也聊到该项目中使用的底层技术原理及方案

底层框架及方案力求使用最少的资源来支撑更庞大的上层建筑。给定一台计算机,它的性能就是1,底层方案占用的资源和性能越少越好,这样在上层的其它应用更有发挥空间

dom事件

为了全局管理,magix在document.body上统一使用代理的方式处理dom事件。同一种类型的事件,比如click,任意view可注册n多click事件,但在magix框架层面,只会注册一次。不同类型的事件,比如click和keydown,在magix框架层面使用同一个事件回调处理函数,减少对象的创建。在这个统一的事件回调里,再根据编译工具提供的相关信息,直接定位相关联的view及调用开发者提供的事件处理程序,无须任何查找算法。

事件代理靠近document的根节点,性能不一定就下降,这取决于统一的事件回调里,如何处理事件冒泡、嵌套等方案。

得益于编译时的方案,在编译过程中,对模板中的事件属于哪个view可以提前确定,相同类型的事件在一个view中有没有嵌套,也是可以提前确定,并把信息传递给运行时。

在运行时,使用编译时提供的信息,事件发生后,无须层层向上遍历dom节点,即可知道当前事件需要哪个view处理,处理完后,向上嵌套的还有没有相同的事件,来决定中断还是继续调用。

其它关于性能的点这里不再一一描述。

做一个高性能的程序是需要从方方面面,点点滴滴积累起来的,任何一个环节都需要仔细,不漏掉任何一个可以优化的点

原生节点拖动时,节点只显示一半

当节点通过添加draggable="true"属性进行拖动时,如果父节点可以滚动,且当前节点未完整出现在父节点里,则如下图所示,拖动时只显示当前可视范围内的内容,而不是整个节点

gs

面板

目前有”元素“、"概览"、”数据源“、”属性“四个面板,可以通过工具栏右侧的按钮对它们进行打开或关闭
image

图中1区域里通过鼠标点击可单独打开或关闭某一个面板,通过2按钮,可以对所有的面板进行统一打开或关闭

同时面板自身也带有相关的操作按钮,如下图
image

可以折叠面板内容或关闭面板。不同的面板折叠后的行为并不相同,比如”概览“面板内容折叠前:
image

当内容折叠后:
image

尽可能的给编辑区腾出地方,不占用编辑区

快捷键

面板的打开或关闭,以及面板的内容折叠展开均有相应的快捷键,可以参考快捷键的说明

通用设计器快捷键大全

对齐操作

元素未处于组合状态

对于未处于组合状态的元素,如果对选中的元素使用工具栏中的对齐工具进行操作,则会根据要对齐的操作,如顶部对齐,找出当前编辑区处于最顶部的元素,然后所有其它元素与该元素进行顶部对齐,其它对齐操作同样的道理。

如果在某个元素上点击鼠标右键,使用右键菜单中的对齐操作,则该次的对齐动作以鼠标下的元素为基准。如顶部对齐,则所有其它元素以鼠标下的元素为对齐元素,进行顶部对齐,其它对齐操作同样的道理。

元素处于组合状态

如果所有选择的元素属于同一个组,则表示对组内的元素进行修改,该对齐操作无论是使用工具栏还是鼠标右键,均同未处于组合状态。

如果所选择的元素属于不同的组
使用工具栏中的对齐工具时,先找出当前对齐操作的参考元素。如顶部对齐,则找出当前选中的所有元素处于编辑区最上部的那一个,然后该元素所处的分组中的所有元素均不动。再从选中的元素中找出相应的顶部最大的元素,当对齐时,其它元素所处的组内元素也一起移动相应的距离。

使用鼠标右键时,参考元素直接变成鼠标下面的元素,后续对齐操作如工具栏中的动作

如果选中的元素中有编辑锁定状态,则不能使用对齐,因为编辑锁定的元素不能移动

如果选中的元素与其它元素组合,组合中有元素处于编辑锁定状态,则不能使用对齐,因为编辑锁定的元素所属的组合不能移动

设计与展示分离

本项目整体分2大部分,设计部分与展示部分。

设计部分

设计部分是核心,编辑器或叫设计器本身要实现历史记录、对齐操作、复制粘贴等功能,同时管理数据和派发事件。
为了便于可扩展和插件化,设计部分的核心就需要健全和简单,这样方便后续核心功能的开发和插件的开发。

目前像顶部的可编辑元素及4个面板全部以插件化的形式存在,这里介绍下可编辑元素的设计与展示分离。

任意一个可编辑元素,如图片,它需要向设计器提供可编辑能力和根据编辑后的数据展示能力。我们可以把这2部分集成在同一个组件里,但这样做,如果离开设计器,做纯展示的话,那组件内部保存的可编辑能力在展示情况下是无用的。

在当前项目里是这样做的:
image

一个组件的编辑能力与展示能力是分离的,在设计器中是编辑与展示均加载

展示部分

而到了展示或打印时,则是这样的:
image
直接加载组件相应的展示层,这样就做到了设计与展示分离,无论在哪种场景下均不加载冗余数据的目标。

相关技术及链接

前端类库

使用magixv5版本,该库由本人所写,开源地址:https://github.com/thx/magix/tree/v5
文档链接:https://thx.github.io/magix/

打包工具

使用magix-composer3.x版本,该库由本人所写,开源地址:https://github.com/thx/magix-composer

组件及样式

该可视化项目所使用的基础组件及样式借签这里,仓库:https://github.com/thx/magix-gallery  文档:https://thx.github.io/magix-gallery/

magix-gallery由本人发起,目前别人在维护

样式和使用的部分组件均全部在当前项目中重写和重构

2024.4.15 由于一些不太好的原因,以上类库或工具已存档私有,如果您需要上述源码,可以联系我获取。无论开源还是闭源,都请尊重别人的劳动成果。

其它

该软件中的所有功能,整体设计和架构以及相关代码均由本人独立完成。使用的第三方组件和库在下方有说明

容器元素

容器元素支持其它元素放在它内部,方便组织更复杂的界面关系
容器元素支持自身嵌套,可以无限级嵌套下去。容器元素目前开发了Table,H-Flexbox,V-Flexbox,如需要,可定制各种不同效果的容器元素。

向容器内添加其它元素

首先向编辑区添加H-Flex等容器元素。如图
image

拖动添加

拖动顶部其它元素到编辑区,当鼠标处于容器格子内时,相应的格子会有可放置指示,如下图
image
松开鼠标后,拖动的元素即放到当前格子内

点击添加

首先点击容器的格子,使格子处于激活状态,如下图
image
此时点击顶部元素时,相应的元素添加到当前激活的格子内

当格子内有其它元素填满,无法使用鼠标点击激活格子时,可以先鼠标点击选中格子内的元素,然后再按快捷键F即可激活当前格子

关于容器

容器元素比较特殊,因为需要承载其它元素,所以没办法像其它元素那样可以点击选中后直接拖动改变位置。
此时可以使用左上角的icon来拖动容器元素,此icon代表了整个容器元素。

关于格子

容器的每一个格子是一个小的编辑区,所以它的大部分操作同大的编辑区,比如右键菜单等,拉框选择等。

因为格子支持拉框选择元素,所以拉框选择与移动冲突,故容器元素不能在格子内按下鼠标进行移动操作,因为该动作被分配给了拉框选择。

通用设计器快捷键大全

在设计器页面,可按shift+/显示快捷键大全对话框,不同的模式快捷键会有少量差别

按键 描述 条件
DeleteBackspace 删除选中的元素 需要编辑区选中1个以上的元素时按下有效
Ctrl+Z 撤销操作 需要有历史记录
Ctrl+Shift+ZCtrl+Y 重做操作 需要有撤销操作
Tab 依z轴从小到大选择编辑区中的元素,当编辑区中的元素过多且有重叠,不方便使用鼠标选择时有用。如果按下Tab同时按下了Shift则按z轴从大到小的顺序依次选择元素 编辑区处于激活状态
Alt 拖动对齐开启的情况下,按Alt在拖动过程中可临时关闭拖动对齐功能,减少对齐辅助线的干扰
Left 左箭头按下时,编辑区选中的元素向左移动1像素。如果按下Left同时按下了 Shift,则向左移动10像素。 需要编辑区选中1个以上的元素时按下有效
Up 上箭头按下时,编辑区选中的元素向上移动1像素。如果按下Up同时按下了 Shift,则向上移动10像素。 需要编辑区选中1个以上的元素时按下有效
Right 右箭头按下时,编辑区选中的元素向右移动1像素。如果按下Right同时按下了 Shift,则向右移动10像素。 需要编辑区选中1个以上的元素时按下有效
Down 下箭头按下时,编辑区选中的元素向下移动1像素。如果按下Down同时按下了 Shift,则向下移动10像素。 需要编辑区选中1个以上的元素时按下有效
Ctrl+A 全选编辑区中的元素或当容器元素格子激活时,全选格子内的元素
Ctrl+C 复制编辑区中选中的元素 需要编辑区有1个以上的元素处于选中状态
Ctrl+V 粘贴剪切板中复制的元素 需要先复制元素
Ctrl+X 剪切编辑区中选中的元素 需要编辑区有1个以上的元素处于选中状态
H 显示或隐藏辅助线 需要存在1条以上的辅助线
Alt+H 清除辅助线 需要存在1条以上的辅助线
Ctrl+G 组合选中的元素 需要编辑区有2个以上的元素处于选中状态,且不属于同一个分组
Shift+G 取消组合编辑区中选中的元素 需要编辑区有1个以上的元素处于组合状态
Ctrl+L 锁定选中元素的编辑状态 至少有一元素处于选中状态
Shift+L 解锁选中元素的编辑状态 至少有一个元素处于选中状态
U 把选中的元素向上调整一个层级 需要编辑区有且只有1个元素处于选中状态
T 把选中的元素调整到最顶层级 需要编辑区有且只有1个元素处于选中状态
D 把选中的元素向下调整一个层级 需要编辑区有且只有1个元素处于选中状态
B 把选中的元素调整到最底层级 需要编辑区有且只有1个元素处于选中状态
F 当容器格子中的元素选中状态下,按下F可聚焦当前格子进行编辑 需要容器格子内的元素有选中状态
C 清除设计区内容
Shift+/ 显示快捷键大全对话框
数字1 打开或关闭元素面板
Shift+数字1 打开或关闭元素面板的内容
数字2 打开或关闭预览面板
Shift+数字2 打开或关闭预览面板的内容
数字3 打开或关闭数据面板
Shift+数字3 打开或关闭数据面板的内容
数字4 打开或关闭属性面板
Shif+数字4 打开或关闭属性面板的内容
数字5 打开或关闭历史记录面板
Shif+数字5 打开或关闭历史记录面板的内容
Shift+Z 打开或关闭所有可拖动的面板,如属性、预览、元素、数据等面板
Ctrl+加号 放大编辑区
Ctrl+减号 缩小编辑区
Ctrl+数字0 恢复编辑区缩放
Space 在设计区直接显示带文字输入元素的输入框 有且只有一个带文本输入的元素选中

表单-输入框

输入框用于收集用户的输入内容,通常配合其它元素来收集用户录入的文本信息

首先从“表单”元素中,添加“输入框”到设计区,如下图

image

它的整体功能和“文本”非常相似,这里重点解释下“属性”面板中几个属性的意思。

image

输入框背后就是一个input标签,如<input placeholder="hoder text" value="initial value"/>

1. 占位文本

input标签属性中的placeholder属性,可用它设置提示输入什么样的内容

2. 默认文本

input标签属性中的value属性,可用它设置默认填充的内容

3. 扩展标记

用于对当前input进行特殊标识,通常与其它程序配合时,方便识别定位。最终以markAs这个key存在于JSON数据中。

4. 扩展样式

设计器会对使用的input标签提供与当前皮肤相同的默认样式,如果您需要对input做视觉上的控制,则可以提供一个样式,如

<style>
.hide-border{border:none!important}
</style>

您需要先把上述样式添加到页面上,然后在该属性里,填写上hide-border则输入框的边框将不可见。

5. 识别名称

用于序列化表单数据时,增加的一个名称属性,方便对数据快速识别。类似<input name/>中的name作用

6.多行模式

用于把input换成textarea以便用户可以换行输入内容

事件

在完成设计,嵌入到页面供用户使用时,所有的表单元素都会向外派发elementinput事件,在该事件内,可以实时获取用户输入的内容。

比如在document.body上添加事件监听

document.body.addEventListener('elementinput',e=>console.log(e));

elementinput事件增加的参数对象如下

elementType: 'form-input',//针对这个输入元素,elementType则是固定死的form-input值
elementValue: userInputValue,//用户当前输入的内容
elementName: inputName,//这是前面第5点提到的识别名称,如果未填写的话,将是一个空字符串
elementInput: input//用户正在输入使用的input框 dom节点对象

IoT 预览器中的 WebSocket 连接如何使用

设计器

在设计器中,服务端数据仅是辅助功能,因此在设计器中使用http接口,未来也将会一直如此,这部分的接口配置可查阅:#27

预览器

IoT 预览器

IoT 对数据要求实时性,因此优先采用 WebSocket 连接,这就需要设计器中的数据源字段与 WebSocket 推送的数据进行一定的配合

在 viewer.html 预览器入口中,可配置 iotUrl 直接为 WebSocket 地址 iotUrl: "ws://your.address" 即可对接真实的服务端数据,目前 WebSocket 的数据格式要求返回如下格式的 JSON

{
    type: 'data'|'heartbeat',
    success: boolean
    message: 'exception message',
    data: object
}

其中的 data 的 key 要求与设计器中数据源中的绑定的字段名称要一致。

同时在首次连接到 WebSocket 服务器时,需要全量推送页面中需要的数据,在后续数据有变化时,可只推送变化的部分,否则页面中未收到数据的可设计元素将展示设计器中给定的初始状态

IoT 预览器中如果对数据实时性要求不高,可支持配置 http 协议的接口,格式如下

iotUrl: "//pull.data.url^//push.data.url"

如果仅展示不需要通过 IoT 预览器向服务器发送数据,则配置成 iotUrl: "//pull.data.url"即可

表单-数据采集

该元素基于"数据-数据表格"构建而来,数据表格帮助信息可参考:数据-数据表格

首先添加“数据采集”元素到设计区

image

因为要在一个表格中完成复杂的操作,所以在左侧会提示相应的行完成什么样的需求

行介绍

它可与下面的标题行配合制作复杂的表头,也可以作为容器,承载其它元素。头支持添加行、拆分、合并单元格等操作。

如果不需要,可以在属性面板中对头进行隐藏

标题行

用于描述数据列的标题,标题行只允许一行,且不允许合并拆分单元格,如果需要复杂的表头,需要和头进行配合使用。

数据行

用于显示静态数据或输入框,用于采集数据,数据行支持添加、删除行,但在只有一行时,禁止删除。同时它也不支持拆分合并单元格

合计行

用于对数据行的求和等功能,它也不支持添加行、拆分单元格等操作

如不需要,可以在属性面板中隐藏

与头的功能相同,支持添加行、拆分、合并单元格等操作,允许自定义复杂的格子。
如不需要,可在属性面板中隐藏

数据采集头、合计、尾隐藏后,就是一个基础的表格

设置输入框

除合计行外,其它单元格均支持设置单元格内容为输入框

image

首先选中某个单元格,在右侧属性面板中,格子内容选择输入框即可

不同的格子内容,下方可供配置的选项不同,输入框的相关配置信息可参考:表单-输入框

至此我们就完成了在设计器中对“数据采集”的介绍,关于使用请参考下方的使用说明

辅助线操作

辅助线用于在某个坐标固定留下一根参考线,用于元素的吸附对齐等其它操作

当鼠标在x轴或y轴标尺上移动时,跟随鼠标会有一个水平或垂直的辅助线,如下图

image

此时鼠标左键单击,会在当前位置留下一条固定的辅助线,如下图

image

固定的辅助线会多出一条拖动手柄,即上图中一小段较宽的部分,该固定辅助线可以通过鼠标拖动这个手柄改变位置。
也可以单击这个手柄激活,手柄激活后显示如下图:

image

显示一个空心的矩形,手柄激活后,可以使用键盘的左右箭头(垂直辅助线)或上下箭头(水平辅助线)按键进行调整。

每按下一次移动1px,如果按下箭头方向键的同时,按下了Shift键,则每次移动10px

临时隐藏辅助线

可使用快捷键H临时隐藏或显示已经存在的辅助线

清空所有辅助线

可使用快捷键Alt+H清空所有辅助线

更多快捷键可参考:通用设计器快捷键大全

如何使用第三方样式和组件

以下讨论需要一定的前端基础

report-designer项目有自己的组件组织方案及打包方案,详情可参考:相关技术及链接。如果你对report-designer自带的组件方案不是很熟悉,则可以使用第三方的样式和组件

report-designer会对整个项目的tsstylehtml做整体编译,针对样式可以识别出哪些样式声明了却未使用,哪些使用了但未声明等,这种方式有利于把report-designer放入任何其它库或框架组织的页面当中。

但这种方式会对使用第三方的样式时稍有影响,下面将会详细讨论

引入第三方样式

以下以引入element ui样式为例,其它同理

假设我们要使用element ui的样式,则根据官方文档链接:https://element.eleme.cn/#/en-US/component/installation

找到从CDN上加载样式的地址:

<!-- import CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">

我们把上述样式地址复制下来,粘贴到我们的入口文件里:index-debug.htmlinex.html等所有需要使用样式的入口文件里。

以下仅展示导入样式的部分代码

<title>Loading</title>
<!-- import CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<style>html,body,.app{height:100%;overflow:hidden;margin:0;padding:0}html{min-width:1300px}@keyframes r{0%{transform:rotate(0)}to{transform:rotate(359deg)}}.logo{width:60px;position:fixed;left:calc(50% - 35px);top:calc(50% - 35px);animation:r 2s linear infinite}</style>

然后你就可以在任意view里面使用第三方的样式了。

消除警告

当我们利用这种方式使用第三方样式时,我们会发现编译工具magix-composer会警告我们在html中使用了未声明的样式,如

image

这是因为如前面所讲,我们的打包工具magix-composer会对整个项目做编译,因为引入的第三方资源打包工具并不清楚,因此会发出这样的警告,如果要消除该警告,可以在gulpfile.js里,通过配置checker检测对象的tmplClassCheck钩子告诉magix-composer不对这样的样式做检测即可。

gulpfile.js里combineTool的checker配置

checker: {//代码检测
        /**
         * 模板中class的代码检测
         * @param {string} selector 模板中使用到的样式选择器
         */
        tmplClassCheck(selector) {
            return selector &&
                !selector.startsWith('el-') &&
                !selector.startsWith('ant-');
        }
    },

这里对el-ant-这样开头的样式不做检查,我们在引入第三方样式的时候,最好找这种以统一某个前缀的样式库,方便做样式处理。

当然这里如果不配置,仅会出警告而已,代码还是正常的。

如果配置后还有相应的检测提示,请联系我

引入第三方组件

可参考项目当中的provider目录下的第三方组件引入方案,可动态加载,也可直接内置代码。

对于第三方组件,需要带销毁功能,否则极容易造成内存泄漏、事件一直监听等问题

使用第三方框架

以下以引入element为例,其它同理

根据官方文档链接:https://element.eleme.cn/#/en-US/component/installation

需要把以下2js文件放在我们的入口处

<!-- import Vue before Element -->
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <!-- import JavaScript -->
  <script src="https://unpkg.com/element-ui/lib/index.js"></script>

report-designerview中,render方法改造如下

async render(){
    await this.digest();
    let vm=new Vue({
      el: '#app',
      data: function() {
        return { visible: false }
      }
    });
    this.on('destroy',()=>vm.$destroy());
}

以上仅示意代码,需要根据自己的需求进行调整。

report-designer自带的magix是和vue类似的组件组织框架,拥有完善的事件监听、参数传递、组件组织等功能,如果可以请尽量使用magix来完成界面渲染和事件处理。magix除了完成必须的界面组织管理外,它还拥有更多的性能处理等技术方案,来保证report-designer在复杂的界面下依然有优秀的性能,因此尽量不要在magix中再使用其它类似的框架。

数据-列表格

该表格元素支持与数据源中某些字段绑定,在需要对报表打印时使用

第1步,添加”列表格“元素到编辑区
image

第2步,点击工具栏右侧的”数据源“面板或使用快捷键数字3(数据源面板目前默认关闭状态)

第3步,在”列表格“元素选中的情况下,可以在数据源面板上方选择要使用的数据源,该数据源需要与后端接口配合,示例中是写死的一份JSON数据。

根据提示,在数据源下拉框中选中一份数据源,则下方显示该数据源可供使用的字段

image

此时可以拖动1中的字段到右侧属性面板中2的区域中,即完成了列的绑定。

已添加过的列目前置灰,不允许重复添加(可不做这个限制)。
当需要删除时,把2中的字段拖出2区域松开鼠标即可删除。其中2区域中的绑定字段支持拖动排序,同时拖动添加时,也可以直接把需要的列放置到想要的位置上

image

编辑区”列表格“绑定数据源后显示如上

当列表格绑定的字段来源不是一个数据源时,会自动清空之前数据源的字段,所以当你想换一个数据源时,不必自己清空绑定的字段,只需要把新数据源的字段直接拖过来即可

打印

当”列表格“绑定的数据源数据较多时,在设计区只会尽可能的显示能够显示的数据。
点击右上方的打印页面时,在打印页面会根据数据源数据的多少,自动排版分页,显示完整的待打印数据。

SVG形状元素

椭圆相关

弧是椭圆的一部分

以椭圆为基础的形状元素目前有“弧”和"圆饼"两个
在设计区添加"弧"后,显示如下
image
可以通过拖动中间虚线圆上的两个角度控制点改变弧的起始和结束角度。
可以通过拖动右侧和底部的两个控制点改变弧的长轴与短轴

当起始角度和结束角度重合时,界面将不显示任何弧,如果需要显示一个完整的椭圆,请直接使用SVG中的椭圆元素

修改点

部分复杂的元素,如“圆柱”、"立方体"、”五角星“、”箭头“等,则支持调整元素上的修改点来改变元素的默认形状

image

拖动元素上的方形修改点,则可以修改元素的默认形状,不同元素的修改点的位置,可修改范围,及可修改方向均根据元素的不同而不同。有些元素的修改点不止一个,如”拐角“元素

image

则有3个修改点

控制点

有些元素由点组成,如”多边形“和”五角星“

image

这些元素可以删除控制点和添加控制点,用来组织想要的形状

控制点之前目前仅支持直线连结

控制点,修改点与属性面板联动

当鼠标在编辑区的元素上点击”控制点“或”修改点“时,相应的空心圆或空心矩形会变成实心,同时右侧的属性面板中,对应点前的标题会带有淡主题色的背景,如下图
image

同样,在右侧属性面板中,当鼠标悬浮在相应”控制点“或”修改点“的标题时,编辑区中相应的”控制点“或”修改点“会变成实心的,告诉用户是哪个点

数据-数据表格

数据表格用于数组数据源的排版及打印,并支持数据汇总功能

从数据元素中,添加”数据表格“到编辑区,如下图

image

因为要在一个表格中完成复杂的操作,所以在左侧会提示相应的行完成什么样的需求

要对表格进行添加、删除行或列,需要先选中一个单元格,然后属性面板中才会出现相应的行或列的添加功能。这是因为不同单元格的行或列添加会有不同的限制

行介绍

标题行

用于描述数据列的标题,标题行支持添加行、拆分、合并单元格等操作,允许自定义复杂的表头,但目前它不支持数据绑定,显示内容需要使用者在右侧属性面板中书写。

数据行

用于与数据源绑定的行,其中仅允许第一行与数据源执行数据绑定操作。绑定操作稍后介绍。数据行不支持添加行、拆分单元格等操作。
灰色的重复数据行告诉使用者绑定数据后,数组数据源的数据在这里展示出来,它不支持操作,仅示意。

合计行

用于对数据源的本页求和,累计求和及本单求和等功能,它也不支持添加行、拆分单元格等操作
如不需要,可以在属性面板中隐藏

与标题行的功能相同,支持添加行、拆分、合并单元格等操作,允许自定义复杂的格子。因为数据表格中间的数据行并不固定,为了放一些固定在表格底部的元素,可以放在底这一行的格子里
如不需要,可在属性面板中隐藏

留白行

用于对底部的留白,以便打印时放页脚等信息。留白可以在右侧属性面板中的“留白”属性中改变高度,或点击使留白处于选中状态,拖动改变高度的手柄进行调整高度。

编辑标题行

首先选中待编辑的标题行的单元格,如下图
image

即可在右侧对相关的单元格进行拆分、添加等操作,并在下方的内容区域可输入相关的标题文字。

绑定数据行

在选中数据表格的情况,点击工具栏右侧的”数据源“面板或使用快捷键数字1
选择合适的数据源,如示例中的报表字段测试

image

拖动1中的数据字段到2中的单元格内,即可完成数据的绑定。

绑定后默认显示文本,如果需要显示成图片、条形码等其它内容,需要选中绑定的单元格,在右侧属性面板中可修改格子内容显示方式。

合计行

选中合计行的单元格,在右侧属性面板中可选择格子的计算方式,如本页累计
image

目前支持本页求和、累计求和及对整体数据求和,其中本页求和及累计求和在打印分页时有用。

打印

点右角的打印,会来到打印页面。在打印页面,会根据数据源中数据的多少,及用户对表格各种行高的设置,自动生成相应页数的打印。

打印控制

image

留白

留白指数据表格底部与纸张底部之间的留白空间,防止表格底部太过于贴近纸张的底部

初始显头

如果开启,有分页的情况下,只在第一页显示数据表格的"头",关闭则每页都显示“头”

最后显尾

如果开启,有分页的情况下,只在最后一页显示数据表格的"尾",关闭则每页都显示“尾”

隐藏头

打印时不显示头

隐藏标题

打印时不显示标题

每页标题

分页情况下,如果开启,则每页均会显示标题行,关闭时,只有第一页显示标题行

隐藏合计

打印时不显示合计行

隐藏尾

打印时不显示尾行

数据检测

开启时,会对进行了数据绑定,但数据接口未提供的列进行删除,否则无论是否有数据均显示

同步宽高操作

同步宽高操作

如果使用工具栏中的宽高同步工具,因为无法获知以哪个元素为准进行同步,所以会算出最大或最小尺寸然后同步,如工具上的提示

image

如果需要精确控制,则需要使用右键菜单

因为是精确控制,所以右键菜单只支持2个元素选中时的同步操作,如下所示

image

以鼠标下的元素为要同步到的目标元素,其它元素为来源元素。

  1. 同步宽

把其它元素的宽度同步给鼠标下的元素

  1. 同步高

把其它元素的高度同步给鼠标下的元素

  1. 同步宽作为高

把其它元素的宽同步给鼠标下元素的高

  1. 同步高作为宽

把其它元素的高同步给鼠标下元素的宽

不是所有元素都支持同步宽高操作,比如表格,因为宽和高都是动态的,则它无法与其它有固定宽高的元素同步,SVG中的部分元素(如贝塞尔,多边形等)也同样的道理,它们都不具备固定的宽高,所以不能使用同步宽高的功能

如果使用工具栏,则元素有编辑锁定时,不能同步,使用鼠标时,如果鼠标下的元素是编辑锁定则不能同步

开发新的元素

如果report-desinger自带的元素不能满足需求,则需要开发新的符合业务的元素

确认元素类型

首先在tmpl/elements目录下为新元素建立自己的文件夹

在文件夹里需要放上desinger.tsindex.ts两个文件,其中desinger.ts是为设计器服务的,index.ts用于展示

如果元素过于复杂,也可以新增一个dshow.ts文件,专门用于设计器中交互和展示

根据元素的功能确认类型,如展示型的,像设计器自带的文本、图片、二维码等元素。交互型的如地图、富文本等,还是带数据绑定的,如单元格,还是带容器类型的,如数据表格、H-Flex等。

report-designer提供了5大类型的元素基类供继承,在这一步根据元素的类型来决定使用哪一个基类来继承即可

填充元素功能

desinger.ts文件里,需要填充上如名称、类型、图标、是否支持拖动修改尺寸、旋转等供设计器使用的属性。

同时需要提供元素自身有哪些属性,以及这些属性在面板中如何被修改

比如元素有一个数量属性,需要使用数字组件来修改它,且最小为2最大为50,则只需要对这个属性使用数字组件来配置,把相应的大小范围等做一个配置即可完成。

设计器提供了如数字下拉框颜色图片等多达40余种属性修改组件来供配置

这一步只需要仿照其它组件进行配置即可,无任何开发工作,至此一个元素就开发完成

高级进阶

只有复杂的元素才需要这一步,根据需求的不同,会稍有代码开发的工作

如果元素本身非常复杂,且属性也无法使用设计器提供的常见组件来修改,则需要开发修改元素属性的组件。

首先在tmpl/gallery目录下增加用于修改元素属性的组件

其次在tmpl/panels/props/element.ts文件里,进行新增组件的注册,完成后新增的组件即可正常工作

如果元素设计器自身在设计器中接收其它操作,如拖放数据、数据推送等,则可仿照其它带有该功能的元素,如数据表格进行开发。

其实这一步和开发普通组件没有任何区别,设计器无非就是把部分属性进行可视化展示和设置。

导出

新开发好的元素在tmpl/elements/index.ts文件中进行导出即可展示在设计器里进行使用了。

接口及配置说明

部分可设计元素支持通过接口与数据源绑定,方便展示或打印,这里介绍下相关的数据格式和绑定思路

接口统一格式

项目中所有用到的接口均按以下统一格式返回

{
    "data":any, //根据接口的具体作用,返回相应的如数组、对象等结构的数据
    "success": boolean,//接口状态,成功为true,失败为false,当失败时,最好在"message"中提供相应的失败信息
    "message": "" //当接口异常时可提供的信息
}

如果您购买了源码,则设计器中所有接口统一由designer/service.ts中发出,而状态success及数据data均可以在这里适配成其它现有接口的格式,无须按这里描述的格式进行处理。

另外一种适配方案是,通过提供的配置项response进行适配,返回上述格式即可,设计器并不要求您一定修改现有接口为上述结构,如果您还是不明白,可以联系作者。您只需要知道上述提到的格式只是设计器默认的数据格式而已,并且可以很方便的适配成其它结构。

全部数据源接口

该接口为设计器数据源面板中,显示的树形列表,如下图所示

image

该接口返回后端都有哪些数据接口可供使用,可查看当前项目中使用的一份写死的格式:https://github.com/xinglie/report-designer/blob/master/apis/fields.json

说明一下data中的格式

{
    "data":[{//数组格式,可设置多个数据源
            "name": "地区测试",//数据源名称,必须设置
            "tag": "apis/area.json",//数据源接口地址、参数或其它标识,非必须
            "id": 1,//数据源id,必须且唯一
            "fields":[{//数据源中包含的可使用的字段
                    "name": "河北",//字段名称,必须
                    "key": "area_13"//字段key,必须
             }]
     }]
    "success": true,
    "message": "" 
}

如上述示例中的”地区测试“数据源,需要配合相应的”地区测试“这个数据接口来看:https://github.com/xinglie/report-designer/blob/master/apis/area.json

其中最关键的是”fields“这个对象,该对象需要列出 https://github.com/xinglie/report-designer/blob/master/apis/area.json 接口中可供使用的字段集合,这是绑定数据源的关键。

对于tag字段,因为开发和发布最终的请求地址会不一样,因此tag可以只存放一个标识字符串,而在最终请求时,report-designer会交由统一的数据模块进行处理,可在这个数据模块里根据一定的规则,把tag存储的标识字符串转换成真实的url。

元素绑定数据源

所有元素均支持数据源的绑定,任意一个元素只要出现在属性面板中的属性也支持数据绑定。目前做了常用元素如文本、图片、条形码、二维码以及“数据”元素分组下的列表格、数据表格等的数据绑定,如果您需要定制元素或调整某些元素的某些属性成数据绑定的形式,请联系作者

元素在绑定时,内部会记录相应的接口地址或标识以及绑定的字段,在展示或打印时,由展示或打印页面根据元素绑定的接口,把数据设置到绑定信息上,而元素仅根据绑定的字段key把相应的数据取出来展示

自动分页

目前只有“数据”元素分组下的列表格、数据表格等能根据数据源的数据多少在打印时自动分页

根据可设计元素的不同,需要绑定的数据源是数组还是对象也不同。比如“数据”元素分组下的列表格、数据表格等要求数组。文本、图片等要求返回单个对象,即使返回数组也只会使用第一个。

更多规则

以上是个人根据以往的经验总结的格式,非必须按照执行的最终格式。包括元素绑定数据源时,到底使用对象还是数组,并非最终定论,可根据具体需求调整。

更换项目里的图标

图标来源:https://www.iconfont.cn/

如何更换图标

  1. 首先登录 https://www.iconfont.cn/
  2. 创建一个空项目
  3. 点击空项目的成员,在弹出的框中搜索 “行列” ,把”行列“添加到这个空项目的成员中
  4. ”行列“这边会把项目中使用到的图标复制一份到您新建的这个空项目里
  5. 得到”行列“复制的图标后,把”行列“移除成员
  6. 接下来您可以在这个项目里添加或删除图标
  7. 图标变更后,根据iconfont.cn的提示,更新图标字段的cdn地址或下载图标
  8. 更换tmpl/assets/index.less 文件顶部的@font-face为您刚才的这个项目即完成更换

调整z轴操作

处于同一个容器内的元素支持调整z轴操作。

使用工具栏

选中要调整z轴的元素,如果支持向上或向下调整时,工具栏中相应的按钮会高亮显示,如图
image

使用右键菜单

右键单击要调整z轴的元素,如果支持向上或向下调整时,右键菜单相应的功能项会高亮显示,如图
image

使用快捷键

选中元素后,可以使用快捷键TBUD四个按键进行调整z轴。
查看所有快捷键

使用组件树面板

image

如上图,编辑区右侧的元素面板,不但显示整个编辑区所有的元素层次结构,同时也支持点击选中元素以及拖动调整z轴顺序。
面板上z轴大的在上方。
只有在同一个容器格子内的元素才支持调整z轴顺序哦,比如不能把H-Flex容器中的元素拖到外面来

静默打印

在网页中要实现静默打印或免去重复设置打印选项等事情,目前需要安装打印插件支持,report-designer有自己的report-designer-server来完成静默打印。

单纯的靠客户端无法完成静默打印,需要在当前电脑或服务器安装report-designer-server,或者其他第三方的打印插件来支持。

这里推荐一下第三方的插件,仅供参考

只要第三方能够接收html打印即可使用,report-designer可以在最后打印时,输出相应的html交与第三方进行打印或使用。

lodop

官方站点:http://www.lodop.net

report-designer中已集成lodop打印功能。

webprinter

官方站点:https://www.webprinter.cn/

可仿照report-designer集成lodop的打印功能修改为webprinter

也可仿照report-desinger-server开发自己的打印服务,只要最终能提供httpwebsocket连接控制打印即可。

欢迎打印第三方与report-designer进行合作。

序号器元素

序号器元素用于在打印时,前端按一定规则生成相应的数字或其它字符串

使用

  1. 首先添加序号器元素到设计区
  2. 选中序号器元素后,在右侧面板会展示序号器元素相应的属性设置

image

显示格式

显示格式用于进一步控制字符串的生成,以适应复杂需求

{#}格式是变量占位符,序号器按下方规则生成序号后,填充{#}所在的位置

计算方式

以何种方式生成序号,目前支持等差和等比计算方式

起始值

生成序号时,从哪个数字开始生成

公差或公比

以等差或等比生成时,下一个数字相较与前一个数字的公差或公比

补零

生成的最终数字是否在前面进行补0,通过补0可确保生成的字符串长度一致

倒序

是否对生成的序号在打印时倒序排列

数据-单元格

单元格元素用于数组数据源的单个排版,批量打印

第1步,从数据元素中,添加”单元格“元素到编辑区
image
可根据需要添加行、添加列、合并单元格等完成需要的界面排版

第2步,点击工具栏右侧的”数据源“面板或使用快捷键数字3(数据源面板目前默认关闭状态)
选择合适的数据源,如示例中的地区数据源

image

第3步,拖动数据源中1区的字段,到编辑区“单元格”元素的格子里,拖到相应格子可以放置时,会有可放置提示

image

松开鼠标即完成了单元格与数据源的绑定操作

image

当需要解除绑定时,点击单元格右上角的删除图标即可,当需要替换绑定数据源或数据字段时,直接拖动新的数据字段到相应单元格即可,元素会根据是否同一个数据源,自动删除旧的绑定

数据绑定后,单击相应的单元格,在右侧属性面板中,可以选择单元格内数据的展示形式,比如展示成文本或图片或其它任意需要的格式。

image

单元格内容类型不同,相应的可设置属性也会不一样。

自动撑高

格子默认最小高度即是编辑区设置的高度,当内容较多时,可以设置由内容把高度撑开,但需要注意的是,当一行上有合并单元格时,即使某一个单元格撑开导致合并的单元格也随之撑开,但它的高度依然是编辑时的高度。

打印

点右角的打印,会来到打印页面。在打印页面,会根据数据源中数据的多少,自动生成相应页数的打印,比如地区数据源有100条,则会生成100页。

迷你模式、自定义排版及设计器插件

迷你模式

该设计器可以嵌入到其他页面中的某个区域内,和现有系统更好的融合

为了保证显示的完整,嵌入的区块请保证至少宽960px

首先在页面中,添加如下的html

<div>
    some other text
</div>
<div style="display:flex;">
    <div style="width:200px">
        left
    </div>
    <div id="app" class="app" style="width:1000px;height:600px;border:solid 1px #ccc;position: relative;overflow: hidden;">
        <div class="outer"><div class="inner"></div></div>
    </div>
    <div>
        right
    </div>
</div>
<div>
    <input placeholder="外部输入框测试" />
     bottom text
</div>

请注意上面htmlidapp的节点,到时候设计器会渲染在该节点里。为了更好的体验,你可以在app这个节点里添加一些loading动画,设计器加载完渲染时,会清除app节点下的内容。

完整示例源码请查看:https://github.com/xinglie/report-designer/blob/master/mini.html
在线demo示例请查看:https://xinglie.github.io/report-designer/mini.html

自定义排版

在设计器中默认的行为中,设计器会提供自身的一些交互方案,比如顶部元素的展示、面板的拖动处理等。

在这些交互的原则中,其中之一就是严格控制设计器显示的内容在指定的区域内,比如面板不能拖动到指定的DOM元素外边来。这是因为宿主页面的界面和交互是未知的,我们不能干扰和影响宿主页面的展示行为。

如果要对迷你模式进行近一步的界面控制,可以使用拆分再重新组织界面的方式。

完整示例源码请查看:https://github.com/xinglie/report-designer/blob/master/split.html
在线demo示例请查看:https://xinglie.github.io/report-designer/split.html

以下讲解下代码和思路

首先组织自己想要展示的HTML结构,如下示例

<div id="toolbar">

</div>
<div id="header">

</div>
<div style="display:flex;">
    <div style="width:150px" id="left">
        left
    </div>
    <div id="app" class="app" style="min-width:1100px;max-width:1100px;height:600px;border:solid 1px #ccc;position: relative;overflow: hidden;">
        <div style="display: flex;align-items: center;justify-content: center; height: 100%;">
            这里可以放一个加载动画...
        </div>
    </div>
    <div id="right">
        right
    </div>
</div>

我们把设计器中工具栏渲染到外部指定的toolbar这个节点上,header同理。

在设计器初始化的代码里,我们可以看到如下的代码

designer.setup({
    rootId:'app',
    mini:true,
    panels:{
        element:{
            to:'left'
        },
        data:{
            to:'right'
        }
    },
    header:{
        to:'header',
        hidden:false
    },
    toolbar:{
        hidden:false,
        to:'toolbar'
    }
});

我们需要指定splittrue,否则会有一些样式上的差异。

同时我们配置了panelsheadertoolbar等选项,选项中to指定设计器中的相应界面展示到哪个外部节点里,hidden指示是否隐藏该界面,比如我想隐藏header又不想把它渲染到外部其它节点中,则可以这样配置

designer.setup({
    rootId:'app',
    header:{
        hidden:true
    },
});

这样设计器自身将不再显示顶部元素栏

后续可以把设计器中的界面拿到外部来展示,进行一定的控制。如果界面改动量比较大,无法通过样式控制完成时,则需要自己二次开发组织相应的界面来供使用。

以顶部元素点击、拖动添加为例,默认是这样展示的。

image

我们如果把它拿到外部其它节点里渲染时,可能不符合交互要求,此时我们可以开发相同功能的面板,即把顶部元素放到一个面板里重新组织展示。

设计器提供了一个元素面板,在面板里展示顶部元素并完成相应的功能,设计器已经自带了该示例,目前界面如下

image

元素面板里面的行为与顶部在功能上一模一样。

后续则可以利用前面提到的,把设计器中的界面拿到外部使用,渲染元素面板到外部节点里。

看过代码你会清楚,像这样功能保持不变,对界面重新组织的需求,只需要继承原来已经实现的View,在html里重新组织下界面就好。

这样我们就可以对设计器进行一定的外部控制,同时可以保护好内部的功能和界面。

setup中,panels配置的面板名称都有哪些?

/**
 * 面板配置项
 */
interface OuterPanelsConfig {
    /**
     * 结构树面板
     */
    tree?: OuterViewConfig
    /**
     * 属性面板
     */
    props?: OuterViewConfig
    /**
     * 概览图面板
     */
    outline?: OuterViewConfig
    /**
     * 历史记录面板
     */
    record?: OuterViewConfig
    /**
     * 数据源面板
     */
    data?: OuterViewConfig
    /**
     * 元素面板
     */
    element?: OuterViewConfig
    /**
     * 调试面板
     */
    debug?: OuterViewConfig,
    /**
     * 动画面板
     */
    animate?: OuterViewConfig
    /**
     * 草稿
     */
    draft?: OuterViewConfig
    /**
     * 资源
     */
    resource?:OuterViewConfig
}

技术点

如何保证js代码与现有页面中的不冲突?

  1. 代码全部采用模块化的方式,设计器本身不向全局挂载和读取变量,只在入口提供designer对象供初始化配置,所以不会冲突
  2. 模块化目前使用seajs的加载器。如果你现有的加载器与seajs的define冲突,可自行修改打包配置,换成如require等其它加载器,目前支持amd cmd iife等加载器规范

如何保证样式代码不冲突?

https://github.com/thx/magix-composer 打包工具会对设计器中使用到的样式做全局编译,且编译时可对生成的选择器做规则定制,比如可以对所有选择器统一添加一个report-desinger的前缀,这样只要你项目中没有以report-desinger开头的样式则不会冲突。

风格统一

目前使用less管理相关的界面皮肤,主题已支持css3 变量,所以界面风格可以定制修改,与你当前系统页面相匹配

界面定制

设计器本身也是采用区块化开发的方式,把一个个界面区块独立开发,然后在入口页面对它们统一布局管理。这样当后期需要对它们重新布局或更新功能时非常方便。

其他像面板,顶部元素面板均可以去掉或定制到其它dom节点内,以和现有的页面更好的整合。

其它技术方案插件

React插件

封装designer.tsx文件

import React from 'react';
let idCounter = 0;
let scriptSource = '//localhost/report-designer/dist/designer.js';

let promisePools = {};
let LoadScript = url => {
    let key = 'script_' + url;
    let p = promisePools[key];
    if (!p) {
        p = promisePools[key] = new Promise(resolve => {
            let script = document.createElement('script');
            script.onload = script.onerror = () => {
                script.parentNode.removeChild(script);
                setTimeout(resolve, 20);
            };
            script.src = url;
            document.body.append(script);
        });
    }
    return p;
};
export default class extends React.Component {
    constructor() {
        super();
        this.state = {
            nodeId: 'designer_' + idCounter++
        };
    }
    async updateView() {
        await LoadScript(scriptSource);
        if (window.designer) {
            window.designer.setup({
                rootId: this.state.nodeId,
                mini:true
            });
        }
    }
    componentDidMount() {
        this.updateView();
    }
    componentWillUnmount() {
        if (window.designer) {
            window.designer.destroy();
        }
    }
    render() {
        return (<div id={this.state.nodeId} {...this.props}>{this.props.children}</div>)
    }
}

使用designer.tsx文件

import Designer from './designer.tsx';
//...

return (
        <>
          <Designer style={{position:'relative',width:900,height:400}}/>
    </>)

vue3插件

vue项目中新建Designer.vue文件

Designer.vue中的内容如下

<template>
    <div :id="designerId"></div>
</template>

<script lang="ts">
    import { defineComponent, onMounted, onBeforeUnmount } from 'vue'
    let idCounter = 0;
    let scriptSource = '//localhost/report-designer/dist/designer.js';

    let promisePools = {};
    let LoadScript = url => {
        let key = 'script_' + url;
        let p = promisePools[key];
        if (!p) {
            p = promisePools[key] = new Promise(resolve => {
                let script = document.createElement('script');
                script.onload = script.onerror = () => {
                    script.parentNode.removeChild(script);
                    setTimeout(resolve, 20);
                };
                script.src = url;
                document.body.append(script);
            });
        }
        return p;
    };
    export default defineComponent({
        setup() {
            let designerId = 'report-desinger-' + (idCounter++);
            onBeforeUnmount(() => {
                if (window.designer) {
                    window.designer.destroy();
                }
            });

            onMounted(async () => {
                await LoadScript(scriptSource);
                if (window.designer) {
                    window.designer.setup({
                        rootId: designerId,
                        mini: true
                    });
                }
            });
            return {
                designerId
            }
        },
    })
</script>

使用

<script setup>
import Designer from './path/to/Designer.vue'
</script>

<template>
    <Designer style="width:1000px;height:80vh"/>
</template>

vue插件2

<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <title>测试</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>

<body>
  <div id="app"></div>
</body>

</html>

<script>
  let ReportDesigner = () => {
    let idCounter = 0;
    let scriptSource = '//localhost/report-designer/dist/designer.js';
    let promisePools = {};
    let LoadScript = url => {
      let key = 'script_' + url;
      let p = promisePools[key];
      if (!p) {
        p = promisePools[key] = new Promise(resolve => {
          let script = document.createElement('script');
          script.onload = script.onerror = () => {
            script.parentNode.removeChild(script);
            setTimeout(resolve, 20);
          };
          script.src = url;
          document.body.append(script);
        });
      }
      return p;
    };
    return {
      data() {
        return {
          designerId: 'rd-' + idCounter++
        }
      },
      async mounted() {
        await LoadScript(scriptSource);
        if (window.designer) {
          window.designer.setup({
            rootId: this.designerId,
            mini: true
          });
        }
      },
      beforeUnmount() {
        if (window.designer) {
          window.designer.destroy();
        }
      },
      render() {
        return Vue.h('div', { id: this.designerId,style:'width:980px;height:400px' });
      }
    }
  };
  Vue.createApp(ReportDesigner()).mount('#app');
</script>

通用方案

也可以像mini.html那样,在入口页面引入designer.js,如,然后在合适的时候,通过调用designer.setup安装和designer.destroy销毁

<!DOCTYPE html>
<html>
<head>
	<title>report designer</title>
   <!--这里引入打包后的文件-->
	<script src="//localhost/report-designer/dist/designer.js"></script>
</head>
<body>
	<!--可以用样式进行修饰设计器的外轮廓-->
	<div id="rd" style="width:1000px;height:80vh"></div>
</body>
</html>
<script>
    //此时widnow上存在designer对象,可以调用designer.setup进行安装
	designer.setup({
		rootId:'rd',//设计器展示在哪个节点里
		mini:true//迷你模式,精简界面
	});
	//在合适的时候进行销毁,这里模拟10s后销毁
	setTimeout(()=>{
		designer.destroy();
	},10000);
</script>

流程图元素

添加元素

可点击或拖动相关流程图元素到编辑区

添加连接线

当元素处于选中或鼠标移上状态时,元素会出现方块形的连接点,目前仅支持通过连接点进行连线

image

当鼠标在任意一个方块连接点上按下时,当前元素的其它方块连接点会自动隐藏,不支持自己连接自己。?该处待讨论,是否可以自己可以连接线指向自己
image

当鼠标移动时,跟随鼠标会显示一个主题色的虚线。
image

当鼠标移动到其它元素上的广场连接点时,虚线会变成实线,表示可以连接。
image

关于移动

当选中流程图中部分元素时,比如[A->B],表示编辑区有3个元素:A元素,B元素及AB之间的一条连接线。如果只选中A或A加上连接线,此时因为B未选中,所以在移动时,A移动且连接线实时更新。
当AB同时选中时,如果连接线未选中,则对于曲线连接的,控制点并不移动。如果是AB及连接线同时选中,则是普通的移动

关于对齐和均分

连接线不能参与对齐和均分,其它元素可以

关于组合

控制线和流程元素不能组合,但对于[A->B],如果AB组合,则它们的连接线自动和AB组合到一起。

关于剪切

剪切只是快速移动元素位置的一种方式,并不改变它的关系,组合中的元素不能剪切。
连接线不支持剪切

关于复制

对于[A->B],如果只是部分选中复制,则连接线并不复制,如果是全部选中,则连接线的关系也一同复制。复制后的新连接线会自动连接到新的元素上。
单独选中连接线不支持复制

关于删除

连接线可以单独删除,如果流程元素被删除,则与它关联的连接线一同删除

其它

因为元素有z轴遮挡关系,所以如果不方便操作时,可以通过TB快捷键,把元素快速置顶或置底,这样会方便操作

只有在同一个容器内的流程图元素之间才能连线
查看容器元素介绍

富文本元素

文本

image

文本元素目前支持简单的富文本,如图所示,如果内容中包含HTML标签,则可以打开富文本选项,进行富文本的显示

文本只支持简单的富文本,对于较长的富文本在打印时并不会进行分页打印,如果需要分页,请参考后续的几个富文本元素

富文本-静态输入

image

对于需要输入较复杂的静态富文本时,可使用ckeditor进行输入,该富文本元素在打印时支持分页打印。

富文本-数据绑定

image

通常富文本会是由服务端指定或生成,此时可以使用支持数据绑定的富文本元素,该元素支持分页打印

富文本-HTML

image

如果用户有一定的编程基础,也可以使用HTML片断,输入自己想要的HTML,且可以和数据绑定配合,以动态生成相应的HTML片断。其中模板引擎选用underscore的template方法。

功能支持

记录将来计划支持的功能及可设计元素

  • 历史记录与快捷键 ctrl+z ctrl+shift+z ctrl+y
  • 标尺支持辅助线
  • 对齐元素
  • 同步尺寸功能
  • 删除元素和快捷键delete backspace
  • 组合元素及取消 ctrl+g shift+g
  • 组合内元素的对齐
  • 组合间元素的对齐
  • 组合及组合间元素的状态及移动
  • z轴调整及快捷键u d t b
  • 编辑区、单个元素、多个元素的右键菜单
  • 缩放支持以及对应的快捷键 ctrl++ ctrl+- ctrl+0
  • 常用面板及插件化
  • 元素面板支持选择元素及拖动调整z轴
  • 容器元素
  • 容器元素的格子选中支持快捷键 f
  • 编辑区网格及吸附网格
  • 组合下的吸附网格移动
  • 通过provider支持特殊元素及第三方插件
  • 概览面板等比显示及支持鼠标拖动改变显示区域
  • 属性面板支持根据元素的某个不同的属性值,控制显示其它属性的显示与隐藏
  • 支持数据源及元素拖动绑定数据
  • 面板支持快捷键及面板内容显示与折叠也支持快捷键
  • 容器中表格的支持,各种行、列合并及拆分
  • 套打支持(编辑区在设计模式下增加背景,同时锁定尺寸,打印模式下去掉背景)
  • 元素支持锁定x或y轴移动,在组合模式下,如果某个元素x或y锁定,则同组内其它元素也只能向一个方向移动
  • 容器支持指定部分元素放在内部,更精准的控制界面编排
  • 元素支持指定放在编辑区或某个容器内,更精准的控制界面编排
  • 打印相关的元素,页头元素,页脚元素,页码元素
  • 自身功能完整的流程设计元素
  • 支持镂空元素,如富文本,把元素内部做为完整的编辑区交给第三方,需处理键盘、鼠标、右键等事件不与中间镂空区域冲突
  • 支持第三方嵌入,可作为元素嵌入,可作为面板嵌入
  • 支持除px外的其它单位,如mm cm等
  • 支持元素向编辑区添加的个数限制
  • 移除元素的编辑锁定,使用工具栏及快捷键,支持批量锁定及解锁
  • 打印分页符,默认和几个设计区高度
  • 打印自动排版,默认,水平铺满,垂直铺满
  • 元素单选时,自动把设计区可视窗口滚动到选中的元素上
  • 辅助线支持键盘方向键微调
  • 元素面板中的组件树名称支持自定义
  • SVG元素支持修改点调整。控制点,修改点与属性面板互动
  • 单元格直接与数据源绑定的表格组件
  • 多编辑区支持
  • 元素动画(类似ppt)
  • 使用原生import动态加载模块
  • 迷你模式,支持嵌入到其它页面局部区域
  • 独立打印仓库与服务
  • 数据绑定支持混合容器
  • 界面根据接口配置动态渲染
  • 样式从less转为css3
  • 对绑定的数据,支持公式编辑或显示控制
  • 打印支持转换成图片或PDF
  • 设计元素支持渲染成canvas或svg,以支持生成更精确的图片或PDF
  • 提供简易RDS,进行服务端图片、pdf以及打印等功能
  • 设计区偏移可视化
  • 数据表格支持头、尾只在初始及末尾显示
  • 保存对话框支持使用json还原编辑区
  • 数据表格计算行支持静态文本和自定义函数处理
  • 打印页进入支持直接显示成图片或pdf。解决思路:根据数据计算出需要动态加载的第三方组件和相应资源,加载完成后再渲染页面和转换
  • 分组表格,支持对指定的相同数据进行分组展示,每个分组支持自己的小计
  • 元素pinX,pinY后,属性面板中需要把X,Y修改输入框变成文字显示或禁用输入功能
  • 元素允许仅显示,不支持选中、修改等操作
  • 支持元素限定在编辑区范围内移动
  • 合并less,以支持修改基础less时实时编译
  • 流程图元素支持自定义背景颜色
  • 单独使用设计器中的某个设计元素进行展示
  • 支持表单元素
  • 数据收集表格
  • 视频元素
  • 使用SVG代替iconfont
  • 地图元素
  • 拖动元素时,自动对齐到其它元素
  • 迁移实时数据元素
  • 进度条元素
  • 拖动对齐到其它元素支持按alt键在拖动过程中临时关闭
  • 支持新建
  • 支持清空
  • todo元素,支持完整的应用
  • 只读模式
  • 快捷文字输入
  • 一次添加多个元素
  • 元素在标尺上的投影
  • 拖动吸附对齐
  • 多维透视报表
  • 组合元素支持属性面板联动修改
  • 修改尺寸时吸附 (格子不支持尺寸修改吸附,元素在按下shift键锁定长宽比时不支持吸附)
  • 表格支持均分宽高
  • 打印支持弹窗预览
  • 支持性能、视频面板
  • 元素面板,以支持超多元素展示
  • 右键菜单增加图标
  • 属性面板支持分组tab
  • 支持克隆
  • 支持分散对布
  • 打印支持份数设置

介绍

可视化设计器

本项目提供可视化设计所需要的基础功能,比如标尺、拖动、旋转、多选、复制等。在此基础之上,设计器中可设计、编辑的元素则由插件化的形式提供,比如需要表格、图片则只添加这2个插件即可,开发人员也可以很方便的定制自己的插件

两种布局

绝对定位

绝对定位布局要求页面宽和高是固定尺寸的,比如需要投放大屏场景,则根据大屏的尺寸设置好相应的编辑区的大小。再比如需要打印,可针对纸张大小,如A4纸设置好相应的编辑区尺寸

基于绝对定位扩展的通用设计器demo:https://xinglie.github.io/report-designer/iot.html

流式布局

流式布局只需要给定宽度或不设置宽度,高度无须设置,整体自适应页面,这种更适用于活动、报表、管理等一系列的线上展示页面

流式布局demo示意:https://xinglie.github.io/page-designer/

以通用设计器为示例,编辑器讲解

该项目之前定位为物联网编辑器,后转为通用设计器。但下面的示例图片仍沿用之前的物联网的名称

image

设计元素

image

可设计的元素在目录 tmpl/elements 下面,可根据需要添加或删除相应的元素,并更新到index.ts中即可。
在插件里面desinger.ts是针对设计器使用的,该文件中指示设计器能设计哪些属性,对元素能否改变宽高、旋转等功能。针对像流式布局需要对设计后的页面做展示时,最终打包的代码并不需要包含designer.ts文件,做到了设计和展示分离

工具栏

image

工具栏提供撤销、重做功能,同时也支持快捷键Ctrl+ZCtrl+YCtrl+Shift+Z
6种对齐方式,需要对齐操作时,需要在设计区中选中2个以上的设计元素对齐按钮才会高亮可用显示
2种同步尺寸的方式,需要同步尺寸时,需要在设计区中选中2个以上的设计元素对齐按钮才会高亮可用显示。默认按选中元素的最大宽或高度同步,如果按下Shift键,则按最小宽度或高度同步尺寸
2种分散对齐方式,同样需要选中2个以上的元素
4种调整z轴的方式,z轴调整只能选中1个元素,如果某个元素已经处于最顶层或最底层,则相应的顶层调整按钮并不会启用
1个删除按钮,需要选中1个以上的元素时高亮可用显示

标尺

image

当鼠标在标尺上移动时,会显示相应的位置辅助线,在标尺上鼠标点击后,会在相应的位置留下一条固定的辅助线。固定的辅助线也可以拖动改变位置以及删除等操作。

如果需要精细的控制辅助线,请参考辅助线操作说明文档:#18

设计区

可直接拖动页面顶部设计元素添加到设计区相应的位置上。也可点击设计元素,则添加到设计区的左上角,然后再拖动到设计区中希望的位置上。
改变设计元素的位置时,支持选中1个或多个,可直接使用鼠标拖动,可以按下键盘UpRightDownLeft四个方向键改变位置。每次按下移动1px,如果在按下方向键的同时,按下Shift键,则每次移动10px

元素面板

image

可查看当前添加到设计区中的元素,同时也支持鼠标移上后,在设计区中高亮显示相应的可设计元素,支持鼠标拖动调整设计元素在设计区中的z轴。
在元素面板某个元素上单击时,则直接选中该设计元素。在单击的同时按下ShiftCtrl键时,可同时选中多个设计元素

概览面板

image

概览面板主要显示整体的布局情况,方便您对整体布局有一个全局观。概览面板中的主题色边框矩形表示当前视窗,同时它也支持拖动改变当前视窗显示的位置

属性面板

image

根据设计区中选中的元素不同,属性面板中展示的可设计元素的属性也不同。
可设计属性在elements/xx/desinger.ts文件中定义(xx表示相应的插件目录名称)
当设计区中选中2个以上元素时,属性面板显示设计区的属性

贴边滚动

image

当拖动元素时,会在编辑区的四周显示淡淡的主题色边框条,当拖动元素的鼠标到边框条上时,编辑区则会向该方向滚动

网格

image
默认编辑区显示背景色及背景的配置,当网格选项打开时,则背景色与背景图隐藏,显示网格选项
网格默认10pxX10px,可自行调整大小,最小4px最大40px,宽与高的值可不同

当网格选项打开且拖动吸附时,拖动的元素则自动磁吸到网格上。
当拖动多个元素时,则以鼠标下的元素为吸附元素,其它元素跟随移动,但不吸附,主要是因为多个元素的间距并不一定是网格的整数倍。

磁吸效果只在拖动时有效,如果通过键盘或右侧的属性面板直接修改坐标信息,则仍以最小单位1px进行。

拖动对齐

添加到设计区中的元素,在拖动时,会和非拖动元素自动对齐,示意图如下

image

水平支持对齐某元素左、中、右三个位置,垂直支持对齐某元素上、中、下三个位置

其中设计区及容器元素的格子也支持拖动对齐,同元素提供的对齐位置

支持吸附到自定义辅助线上

更多信息可参考:拖动对齐操作

绝对定位编辑器快捷键大全

相关示例

绝对定位布局

https://xinglie.github.io/report-designer/absolute.html

流式布局

https://xinglie.github.io/report-designer/page.html

通用设计器

https://xinglie.github.io/report-designer/iot.html

获取源代码

请联系微信:qq84685009

动画

概述

设计器元素支持应用@keyframes指定的css动画,您可以把常用动画封装成单独的css文件,供设计器使用。
设计器目前以插件的方式使用Animate.css提供的动画,您可以通过https://animate.style/访问Animate.css官网了解更多信息

动画面板

动画面板默认不展示,您需要通过设计器右上角的面板列表中找到动画面板通过点击图标打开,或使用快捷键:数字6按下后打开,具体动画面板的图标及位置如下图所示

image

打开后动画面板将会展示在设计区的下方,如下图所示

image

操控区域

操控区域用于设置动画时间轴的长度、间隙、重置及播放等功能

image

通过上图1下面的数字框,您可以修改时间轴的长度。目前默认时间最小时长为10s,最大时长为60s。可通过配置修改
通过上图2下面的数字框,您可以修改时间轴上最小时间的间隔。目前默认最小10px,最大20px。可通过配置修改
通过上图3下面的按钮,您可以停用或启用动画功能。该按钮方便您查看元素在应用动画前是什么状态,应用动画后是什么状态。
通过上图4下面的按钮,您可以一键重置时间轴到起始区域。
通过上图5下面的按钮,您可以让设计器播放或暂停动画,来查看整个时间段内或某个时间点的动画效果

时间轴区域

通过时间轴您可以拖动时刻线查看某个时间点动画的效果

image

时刻线在拖动过程中,左、右两侧会出现自动滚屏提示区域,当您把拖动的设备如鼠标放置在该区域内时,时间轴会自动向右或左进行滚屏,无须您松开鼠标移动时间轴后再拖动。

元素区域

通过元素区域您可以查看当前设计区哪些元素应用了哪些动画,同时您可以添加、修改、删除某个元素的某些动画

image

目前设计区内的元素在支持动画的情况下,将会自动出现在元素区域内(这个可通过配置修改为仅选中元素且支持动画的元素才出现在元素区域内)。

上图1区域内的元素,会自动联动设计区元素的状态作出相应的展示,如元素只读、禁用、启用等状态。同时为了方便您的查看,当您把设备如鼠标放置在1区域内的元素上时,设计区内的对应的元素会自动出现遮盖提示,如果您还是无法确定是哪个元素,也可以通过点击使设计区内的元素选中。

通过点击上图3区域,将会打开添加动画的弹框,您可以在弹框内选择需要的动画,添加后动画将会出现在2区域内。添加动画的弹框将在后面介绍。

上图2区域内添加的动画,您可以通过点击重新打开动画选择框,进行动画的修改或替换。当您的设备如鼠标悬停在该区域内时,会出现删除按钮,如果您对该动画不满意可以删除。

上图2区域内的眼睛图标用于控制该条动画是否应用在设计区内供预览。比如您可能一次性为同一个元素添加了多个动画效果,该元素在设计区内呈现的效果是这些动画叠加后的。如果您想查看某个动画暂时去掉是什么效果,您可以点击眼睛图标,使它变成闭眼状态,则元素将不应用该条动画。通过该功能使您可以快速查看该条动画效果添加或删除后是什么效果,而不必通过反复的添加或删除相应的动画进行查看。通过点击闭眼图标可以再次应用该条动画效果。

上图2区域内的锁图标可锁定或解锁当前动画轨道的编辑,如果您对某条动画编辑满意后,可点击锁图标进行锁定,防止误修改。

动画轴区域

通过动画轴您可以调整动画效果在时间轴上的位置及时长

image

上图1以品牌色出现的是操控区域,您可以通过拖动3下方的手柄调整动画时长,可拖动4上方的区域调整动画在时间轴上的位置。
上图2区域以灰色表示的则是该动画的重复次数,方便您明白动画在时间轴上的效果,如果动画只播放1次,将不会展示灰色部分。

选择动画弹框

通过动画框选择事先准备好的动画效果

image

通过上图1区域内您可以挑选需要的动画效果,当您选中后,相应的效果将会在上图2区域内进行预览,方便您把动画添加到动画面板前查看相应的动画效果。

通过点击1区域内的动画名称,您可以进行单选动画,如果您需要选择多个动画,则需要按下Shift键后再点击,此时您将会选中多个动画。

上图1区域内左上角带有f角标的动画,表示该动画部分时间阶段内置了动画函数,您在上图3区域内的动画曲线配置只会应用到部分动画阶段。

上图1区域内左上角带有品牌色f角标的动画,表示该动画全部时间都内置了动画函数,您在上图3区域内的动画曲线配置将不起任何作用。当您选中带有品牌色f角标的动画时,上图3区域内的动画曲线将自动进入禁用状态,避免您不必要的操作。

上图3区域内您可以对动画进行详细的配置,如果您对这些配置不太了解,默认就好,不需要做修改。
为了您更好的控制动画,建议您前往MDN阅读动画章节(https://developer.mozilla.org/zh-CN/docs/Web/CSS/animation)

上图4区域下方的按钮,您可以确定当前动画的配置或取消关闭弹框

常见问题

如何替换Animate.css

第一步:在入口文件如index.html中删除或替换Animate.css
第二步:在report-designer/tmpl/panels/animate/picker.ts更新替换后的动画名称即完成替换

如何添加自己的动画库

第一步:把自己的动画写入单独的样式文件内,注意名称不要与现有的冲突
第二步:在入口文件如index.html中像引用Animate.css一样,引入您的动画样式文件
第三步:在report-designer/tmpl/panels/animate/picker.ts添加您样式动画文件内的动画名称即完成添加

为什么有些动画添加后,设计区变大了

这是因为部分动画效果使用了translate,而Animate.css又写的值比较大,所以会撑开设计区域

为什么应用动画后,元素的旋转或透明失效了

首先确定的是元素旋转或透明没有失效,而是被动画里面的旋转和透明覆盖了。

以旋转为例,比如把元素旋转了30度,而应用动画后,动画在开始的时刻又把角度旋转到了0度,此时元素呈现的就是动画旋转的0度效果。这也是前面操控区域里面介绍的3区域下方按钮的作用,方便您停用动画查看未应用动画的效果。

可以让动画以元素当前旋转角度或透明度做动画吗

可以,把当前角度或透明度以变量的形式在元素上提供,动画里以该变量为起点做动画即可。
目前Animate.css并未提供该功能,后续如果设计器提供自己的动画库时会这样做。

皮肤与多语言

image

皮肤

如果需要修改设计器的外观,可修改tmpl/assets/_var.less提取的变量即可,具体可查看文件内的变量注释。

通常只需要修改品牌色,其它颜色自适应,如果有较强的视觉要求,则可自行调整其它颜色变量

多语言

项目支持多语言设置,但目前仅提供了中文包,如上图。

可自行提供如英文包放入并在tmpl/i18n/index.ts中引用即可

更新

2022-08-16已支持英文语言包,目前用于测试,只做了少量翻译。设计器实现的是无刷新语言切换,只需要仿照现有的语言包扩展其它语言即可完成多国语言版。

风险点:中文翻译成其它语言时,有可能会很长,导致界面不美观,需要根据不同的语言进行适当的界面调整

基础元素数据绑定

该issure仅说明文本、图片、平铺图、二维码、条形码的静态内容和动态数据绑定相关的操作

对于基础元素,文本、图片、平铺图、二维码、条形码目前也支持数据绑定,但仅支持绑定单条数据,不支持绑定数组。

以下使用文本元素来介绍相关的静态内容和数据绑定操作,其它元素类似

静态内容

在选中文本元素的情况下,可通过属性面板中的内容输入框输入相应的静态内容,也可以在选中文本元素的情况下,按下Space,即空格键,则文本元素上方会显示输入框,此时输入静态文本即可。

其它元素,图片、平铺图、二维码、条形码目前只能在属性面板中输入静态内容

绑定数据

添加文本到设计区

image

如果数据源面板未打开的情况下打开数据源面板

通过图示中的2选择合适的数据源,在3区域选择合适的绑定字段,鼠标按下相关的字段,拖到属性面板中4后面的矩形区域内,即完成了数据绑定。

也可以在3区域选择合适的绑定字段,鼠标按下相关的字段,直接拖到1,文本元素上,当拖到文本元素上时,会有相应的界面提示,如下图
image

此时松开即可完成数据绑定

解除绑定

当需要解除元素的数据绑定时,需要在属性面板中,如上上图中的4后的矩形区域内,把相应的字段拖出矩形区域松开鼠标即可完成解除绑定。

静态内容与数据绑定

目前这些基础元素即支持静态内容的输入又支持动态数据绑定,当二者存在时,如何显示?

在设计器里,二者的行为是互斥的,即当你使用静态内容后,数据绑定功能会被隐藏和禁用,在这种情况下你无法完成数据绑定。同样当数据绑定后,你也无法使用输入静态内容的功能。

在展示或打印的时候,如果你在数据中即指定了静态内容又指定了绑定数据,则优先展示绑定的数据

打印与内容转换

浏览器并不能准确的把设计器设计出来的内容打印出来,即使使用chrome,在对内容溢出隐藏的情况下打印,效果并不像网页中见到的那样。
要想获得更精确的打印,需要先对内容做转换,转换成图片或pdf。其实有了图片,转pdf是非常容易的,所以要优先生成图片。
目前在打印页面的内容转换存在诸多问题,后续提升可考虑以下3种方案。

方案1

最终渲染时,抛弃传统的dom方式,把所有可设计元素画在canvas上。这样后续在转换图片或pdf时,均不会产生错位、边线粗细不一等问题。

因目前所有数据均为json描述,因此通过该方式仅仅是开发工作量的问题,技术上并没有难度,只是工作量较大且周期长,需要有人协助。

方案2

服务端渲染转成图片。全在网页客户端里处理,开发工作量大,那就通过接口借助服务端的能力。

可向服务端提交json数据或完整的html,由服务端生成相应的图片,具体生成方案服务端决定。

这样的工作量全在服务端,需要综合评估客户端与服务端的工作量,看采用哪种方式更省时间。

方案3

通过开发客户端或命令行的形式,如使用nodejs,一方面提供httpwebsocket用于与设计器通讯,一方面使用如puppeteer的插件,在收到任务后,把网页内容转换成图片并回传给设计器。

目前感觉可使用方案3,在nodejs下快速开发测试一下。

设计器与预览器

分离

目前设计器与预览器分别打包发布,虽然可以直接在设计器中把预览器集成进去,但这样不利于单独使用预览页面及后续的预览器扩展

设计器

设计器支持绝对布局和流式布局,因二者布局相差较大,故分仓库开发维护。
当前仓库是绝对布局的设计器,流式布局的可参考这个仓库:https://github.com/xinglie/page-designer
目前精力主要在绝对布局的设计器上,即该仓库的项目

设计器的目标是尽可能支持同质化的所有需求,包括不限于报表、海报、打印、物联网等所有2d场景下的可视化设计
未来会支持3d的场景设计

预览器

同样的设计数据,不同的预览器,最终展示会有差异。在不同的业务场景下需要做不同的加工,为了使用方便,对预览器进行了分类处理

页面预览器

页面预览是最简单的对设计数据的还原,因开发没有任何难度且需求非常小众,该预览器未集成到设计器中,如果需要使用可参考:#38

固定分页预览器 (viewer.html使用参数 use=default)

设计器中默认使用是固定分页预览器,根据页面大小,自动对需要分页展示的设计元素进行分页处理。
该固定分页预览器会对特定元素如数据表格进行分页展示,分页后其它不分页元素会固定重复。

该预览器常用于多列分栏且分页后部分元素固定重复展示,这也是设计器默认的预览器

该预览器下所有数据使用http接口

流式分页预览器 (viewer.html使用参数 use=stack)

对填充数据后的元素进行流式分页,比如数据表格填充数据后,其它元素会相对填充数据后的元素进行位置偏移分页。

该预览器常用于报表或订单类型的打印,上下排列且内容会随数据变化的情况下使用。

该预览器下所有数据使用http接口

标签分页预览器(viewer.html使用参数 use=label)

该预览器针对标签纸所做,通常设计阶段只针对一个标签设计,在打印时,需要根据配置,一行打印多个标签。

该预览器下所有数据使用http接口

试卷预览器(viewer.html使用参数 use=exam)

对常用试卷(A3横版)做的预览器,移除不必要的按钮,让预览器更专注

IoT预览器(viewer.html使用参数 use=iot)

设计器中也提供了IoT预览器,默认使用WebSocket进行数据交换,同时在该预览器里对数据交换层做了抽象处理,支持包括不限于ws、http、mqtt等协议,无论您使用哪种协议,均可以直接对接

大屏看板(viewer.html使用参数 use=board)

如果没办法像Iot预览器那样使用WebSocket进行数据交换,可以选择该模式使用客户端轮询的方式进行数据更新。

该大屏模式下仍然使用传统的http接口,但加入了自动轮询请求的功能,同样可以更新元素绑定的数据,是对无法使用WebSocket时的一个备选方案

单元格不能自动换行

version 202106220825
单元格绑定了一个长字符串,结果点打印时预览发现不能换行,属性那里也没有自动换行,在保存的stage中发现autoReturn为true。
另外使用数据表格的时,能够自动换行,但绑定内容的宽高并不像单元格一样随着单元格的宽高变化。单元格高度增加/减少了,但绑定内容没有增加/减少。修改单元格的宽高之后还需要再去修改里面绑定内容的宽高。这个是有意为之的吗?

拖动对齐

添加到设计区中的元素,在拖动时,会和非拖动元素自动对齐,示意图如下

image

其中平铺图片是正在拖动中的元素,当元素的顶、中间、底边以及左、中间、右边与另外元素的关键边靠近时,会自动吸附对齐。

吸附阈值目前设定为8px,越小越容易脱离吸附,越大越不容易脱离吸附。

当元素旋转后,将以元素所在的矩形进行相应的对齐处理。无论旋转与否,均支持4个顶点、4条边中间点及元素中心点,水平9条垂直9条线的对齐方式。为了解决对齐线过多的问题,目前支持顶点、边中间及中心点通过配置来决定是否参与对齐。

组合元素及多选元素

优先鼠标下的元素参与拖动对齐(可配置为全部移动元素为拖动对齐),仅非移动元素才能成为吸附对齐对象

容器元素

容器及子元素如果不参与移动,则所有子元素也可以成为对齐吸附对象。

网格与吸附网格

网络功能已下线,代码已注释,如果有需求可自行放开网格代码的注释
当吸附网格与拖动对齐均打开时,对齐优先级高

设计区

设计区及容器的格子(小设计区)也支持拖动对齐

alt键

在拖动过程中,如果按下alt键,则可以临时停用吸附对齐。如果通过设计区配置为默认拖动不对齐,在拖动过程中,如果按下alt键,则可以临时启用吸附对齐

请问如何与后端动态数据结合进行打印呢?

感谢大佬这么棒项目!
目前发现打印方式只能在打印的页面 复制粘贴模板JSON,然后进行打印,找了下没发现相关说明,想请教下我能不能在我的系统定义一个打印按钮,点击后传入后端的动态数据打开打印界面进行打印呢

绑定元素控制显示

对于像”文本“及数据集合下的”单元格“、“数据表格”等支持数据绑定的元素,在把数据源中的数据显示出来的时候,需要对显示格式进行控制

因为对绑定的数据显示控制通常只发生在“文本”上,比如需要把数字显示成千分位、时间戳显示成友好的时间格式等,以下仅说明
“文本”的显示格式操作。其它元素的数据绑定,后期如果需要对数据源进一步加工,则会增加钩子的形式来进行二次处理。

首先添加支持显示“文本”格式及数据绑定的元素,如数据分组下的“单元格”元素,添加到设计区。

显示格式仅用于对绑定后显示数据时的控制,因此需要先绑定数据才能使用

以下是设计区添加“单元格”元素并对某个格子绑定“报表字段测试”中某个字段后的效果

image

可以看到我们对第一行第二列单元格绑定了“报表字段测试”中的“点击量”这个字段,右下角“文本”图标指示当前格子中数据最终会以文本的内容方式显示。你可以在属性面板中根据需要,修改成图片,二维码等其它最终需要显示的内容。

在格子内容是“文本”的情况下,在下方有一个“显示格式”的属性,如下图
image

根据提示,点击后弹出“显示格式”的对话框

image

可以根据数据源是什么样的数据,如数字、时间、货币等,选择自己喜欢的格式控制,点击应用即可,比如我们选择数据中,整数部分使用千分位格式。

最后点右上角的打印,来到打印前使用真实数据填充的页面,可以看到真实的数据根据千分位的格式要求,展示如下

image

在显示格式里,如果没有自己想要的格式,可以进行定制,把常用格式内置进去。
也可以使用自定义,自定义是一个函数,可以完成任何需要的格式,自行体会。

浏览器兼容及相关问题

浏览器

代码编译降级到es2018,所以需要除IE之外的其它主流浏览器才可以,未来也不会支持IE,当然ChromiumIE应该是支持,这个没有测试

不同浏览器如chrome、safari、firefox,以及同浏览器不同版本渲染出来的最终界面并非完全一致,渲染结果以最终使用的浏览器为准,以及相关的打印功能。

编辑区

编辑区大小限制为10000px X 10000px ,以配置的形式提供,可自己调整

编辑区元素个数

先定义3个概念

卡死

指程序需要大量的运算,界面无法响应用户的任何操作。如点击某个按钮,浏览器失去响应,此时不能使用键盘输入,不能使用鼠标滚动页面等。后续处理完运算后,才能继续响应用户的操作

卡顿

指程序需要大量的运算,界面不能及时响应用户的操作。如当前程序正在后台运算,用户使用键盘输入时或滚动页面时不流畅。

异步更新

技术方案采用数据与界面分离的方式,数据变化后界面不会立即变化,而是使用分割任务异步更新。异步更新的速度取决于电脑的性能,性能越好异步更新的越快。

卡顿时用户仍然可以继续操作,但卡死时不可以

该可视化脚手架采用前后端分离的架构,使用JSON交换数据。
同时可视化的技术方案采用数据与界面分离的方式,用户的所有操作及时更新JSON数据,同时再异步更新界面。

这样只需要1JSON数据既可以方便存储同时也方便还原界面。

当编辑区元素添加越来越多时,程序需要更多的内存保存数据,同时异步更新界面的时间也会变长,卡顿现象可能会出现。理论上越多元素,越容易出现卡顿,浏览器消除卡顿时间也越长。

但无论如何,不会卡死。

像我这台电脑,添加200个图表或500个普通元素后,全选拖动有卡顿出现。

image
为了更直观的告诉用户当前程序是否处于后台更新繁忙阶段,当出现不能及时更新界面时,会在程序的状态栏最右侧显示“系统正忙...”标识,告诉用户程序已经开始出现卡顿,不能及时响应您的操作了。 

你可以试一下自己的电脑,看添加多少元素后会出现提示

性能上限

即使采用异步更新,由于不同的电脑和浏览器,当元素过多时,report-designer无法保证一定提供流畅的操作。

选择元素操作

拉框选择

鼠标在编辑区中按下,然后拖动。鼠标会拖出一个虚线矩形方框,当鼠标拖出的虚线方框与元素所在的矩形相交或包含关系时,则对应的元素进入被选中状态。
当元素被旋转后,部分元素所在的矩形也会随着旋转,同样旋转后的矩形也需要与鼠标拖出的虚线矩形相交或包含时,元素才进入被选中状态

鼠标在拉框选择的时候,按下了Shift键,则本次拉框选择会叠加之前选中的元素。即进入多选模式

当拉框选择遇上容器元素

  1. 当鼠标在某个容器格子内按下时,仅支持拉框选择当前格子内的元素,不支持框选当前格子外的元素(因格子可以无限嵌套,如果要框选格子外的元素,计算量太大)
  2. 当容器格子内有元素选中时,且按下ShiftCtrlCmd多选时,鼠标在容器元素外框选,当框中容器元素时,容器格子内的元素选择被取消(当容器被选中时,它所包含的子元素会取消选中状态)。当框选到容器元素后又取消时,容器格子内的之前选择的元素则会自动再次被选中

任何操作如果有可能给用户后悔操作时,均有后悔操作。所有功能如果可以给快捷键时,均有相应的快捷键,尤其对于一些不便于鼠标操作时,肯定有对应的快捷键。

鼠标点选

鼠标直接点击目标元素,目标元素进入选中状态。
在按下ShiftCtrl的同时,使用鼠标点击目标元素,进入多选状态。当目标元素处于未选中状态时,进入选中状态。如果目标元素已处于选中状态,会取消选中状态。

tab键

z轴从小到大选择编辑区中的元素,当编辑区中的元素过多且有重叠,不方便使用鼠标选择时有用。如果按下Tab同时按下了Shift则按z轴从大到小的顺序依次选中元素

结构树面板

image

当鼠标hover在结构树面板中的元素时,会在编辑区显示一个半透明主题色矩形,告诉使用者当前对应是编辑区哪个元素。
当鼠标单击结构树面板中的元素时,该元素进入选中状态。
在按下ShiftCtrl的同时点击结构树面板中的元素,进入多选状态。鼠标下的元素如果处于未选中状态时,进入选中状态。否则会取消选中状态。

当元素处于单选、多选、组合等状态时,其被选中元素的边框会表现出不同的显示,方便使用人员进行区分。

打印

设置纸张

如果需要打印设计,需要把打印设计器的尺寸从默认的px(像素)单位转换成mm(毫米)单位,这是因为不同系统单位尺寸内所容纳的像素数是不一样的,如果使用px有可能造成打印尺寸不正确的问题

打印设置

在设计器中设计好的元素,在打印时会自动计算出每一页的位置和数据,所以务必把打印机默认的页边距等之类的设置为0

分页打印

打印时,会自动把设计器中的元素按设置好的页面大小进行分页,因此您应该直接把页面尺寸设置为目标纸张的尺寸,如A4A3,这样在打印分页时就不会被浏览器自动把元素放到错误的分页上

分页打印时,请使用chrome浏览器,其它浏览器可能会有问题

打印及单独渲染

该功能方便把设计器中元素的渲染层拿出来,放到其它页面中单独显示

功能背景

在设计器中设计好元素后,比如数据表格,进行了相关的行和列的设置,绑定了数据源,默认它可以在设计器附带的打印页面中展示。

如果想把这个数据表格单独拿出来,放在现有的页面中,则可以使用该功能。

使用方式

首先在设计器中对需要单独展示的元素做设计,完成后单独拿到该元素的JSON描述数据。

比如我们使用文本元素来在其它页面中单独展示,则在设计器中只放一个文本元素进行设计。完成设计后,在示例页面中点击保存,弹出展示编辑区数据的对话框,在这里面找到文本元素的JSON数据,把该元素的这个完整的JSON数据拿出来,以备在其它页面中使用。

image

示例图片中截图即是某一个文本元素的完整JSON数据描述

配置目标页面

线上demo页面:https://xinglie.github.io/report-designer/standalone.html
源码页面:https://github.com/xinglie/report-designer/blob/master/standalone.html

打包时,会对项目中的所有元素展示层单独打包到dist/printer.js文件里,方便在其它页面引入使用。

比如在示例页面中,先引入独立使用的js文件dist/printer.js,然后调用printer.element方法,则会在相应的节点里,把指定的元素展示层显示进来。

printer.element接受2个参数 printer.element:(nodeOrId,elementJSONData)=>void

nodeOrId:页面上渲染元素的DOM节点或节点id
elementJSONData:元素数据,即前文提到的该元素设计器中产出的JSON数据

该方法无返回值

如果元素本身绑定了数据,则需要外部请求数据,和JSON数据一起传进来,因为设计元素的展示层自身并不请求数据,需要外部准备好。

如果需要对元素拆分,则需要自己根据props的配置信息,自行拆分,多配置几个节点即可。

并非所有的元素都支持单独拿出来使用,比如页码器,比如流程图中的连接线等,当然,这些元素在传入属性时,补充好数据也是支持单独使用的

组合操作

只有处于同一个容器内的元素才支持组合操作

因为容器内的元素使用坐标是相对于容器的,所以只有同一个相对坐标固定的才可以使用组合。比如同一个编辑区内的元素或同一个容器的格子内。

组合支持工具栏、右键及快捷键进行组合操作

同一容器内的元素,即使不同的格子,也支持组合操作。

关于移动

组合后的元素移动其中一个,其它元素也跟随移动

关于对齐

当组合内的元素与组合外的元素有对齐操作时,组合内的元素整体移动,保持相对位置
当组合内的元素对齐时,此时表示修改当前组合内部分元素的位置,按对齐操作进行相应的位置移动

修改单个元素的属性

组合后的元素,支持选中其中一个元素,通过右侧属性面板修改其中的属性,当通过面板修改时,修改仅影响到当前元素。如修改x,y坐标,则只有当前选中的元素移动。

这样做的目的是:可以对组合后的元素有一个修改的反悔空间,像x,y坐标的修改,如果修改当前选中的元素,组合中的其它元素也一起修改,就会造成就想单独移动一下组合中的某个元素,就需要先解散再修改,十分不便。如果选中一个但想同时移动整个组合,可以使用鼠标、键盘来整体移动,属性面板则作为单个修改的通道。

关于删除

可以选中组合后的部分元素进行删除,当删到组合中只有一个元素时,则自动解除组合

读写与权限控制

需求背景

在有权限设置的系统里,比如管理员和普通用户,管理员可预设一些必须存在的元素及元素位置,普通用户可在此基础上增加其它需要的元素。
管理员添加的元素在普通用户那里只读,不能调整位置、改变大小、删除等操作

功能使用

设计器已支持元素只读,可查看只读示例,只读需要通过代码来设置,目前无法通过界面指定

操作细节

z轴

只读元素无法调整z轴,但普通用户新增的元素支持和这些只读元素调整z轴,毕竟谁遮盖谁需要交与最终使用者决定

容器

容器在只读的情况下也允许向格子内增加元素
如果容器元素未设置只读,而子元素设置只读,则容器可以被删除,连同只读的子元素。可设置为子元素如果有只读,则不允许删除容器

拖动对齐

只读元素可以提供对齐功能,方便在拖动其它元素时与这些只读元素对齐

清空设计区

如设计区有容器,且只读,但容器内放入了普通元素,则清空时,清除所有非只读元素

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.