生态产品多个设计器现已发布候选版本

查看生态总览
跳转到内容

设计器 ​

<xh-flow-designer> 编辑流程定义:从物料面板拖入节点、拖动端口连线、在属性面板里改属性,问题列表随时给出校验结果;撤销重做、剪贴板、对齐分布与自动布局都是内置命令。它只改流程定义,不保存,什么时候存、存到哪里由宿主决定。完整的属性、方法、事件与命令见设计器参考。

放上页面 ​

调用 defineFlowDesigner() 注册设计器,它用到的 XiHan.UI 元素与设计器零件一并注册。样式表比查看器多两份:设计器零件的 @xihan-ui/designer-shell/style.css 与设计器自己的 @xihan-flow/designer/style.css。

ts
import { defineFlowDesigner } from '@xihan-flow/designer'
import { extensions, schema } from '../schemas/approval'
import '@xihan-ui/styles/index.css'
import '@xihan-ui/designer-shell/style.css'
import '@xihan-flow/runtime/style.css'
import '@xihan-flow/designer/style.css'

defineFlowDesigner()

再把扩展与流程定义交给 extensions 与 schema。设计器铺满自身,宿主要给元素定高度。

ts
const designer = document.createElement('xh-flow-designer')
designer.extensions = extensions
designer.paletteGroupOrder = ['human', 'control']
designer.paletteGroupLabels = xihanWorkflowZhCN.categories
designer.propertyGroupOrder = ['basic', 'participants', 'form', 'execution']
designer.propertyGroupLabels = xihanWorkflowZhCN.groups
designer.schema = schema
  • extensions 与查看器的是同一类扩展,设计器层的扩展还能登记属性编辑器、命令与面板,见扩展。同一个扩展对象换了引用会当作卸下再装上,宿主要保持引用稳定。
  • 给 schema 赋值即装载:清空撤销历史与选择,视口按实际大小(100%)摆好——整张图在某个方向上放得下就居中,放不下就从左上角留白处起,开头的节点在眼前;画布尺寸变了照这样重摆,直到设置视口或在画布上按下指针。要看全貌用「适合窗口」(zoomToFit())。赋回当前这一份不会重新装载。
  • 读 schema 得到当前的流程定义。设计器从不改动传进来的对象,每次修改都产生一份新的定义。

编辑 ​

新建节点:把物料面板里的节点类型拖到画布上,在松手处新建;点条目则在视口中央新建;中央已有节点时顺着右下方错开一步,连点几次不会叠在一起,错到视口边上从左上角另起一列。物料按节点类型的类别分组,paletteGroupOrder 定类别的先后,paletteGroupLabels 给类别换显示文字。宿主也可以调用 addNode(type, at)。

选择:点节点或连线选中它,按住 Shift、Ctrl 或 ⌘ 点击则加选或取消;在空白处按住 Shift 拖出框选,同时按住 Ctrl 或 ⌘ 时追加到已有的选择上;点空白处清空选择。选择变化派发 selection-change,getSelection() 与 select() 读写选择。

移动:拖动选中的节点。grid 是吸附的网格边长,缺省 16,写 false 不吸附;设置里的「吸附对齐」开着时(缺省开,见设置)靠近其它节点会吸附并显示对齐线。按住 Alt 松手时原节点不动,在新位置复制一份。画布有焦点时方向键微移选中的节点,按住 Shift 一次移一格网格。

连线:从节点的端口拖到另一个节点的端口。拖动途中预览线标出能不能连;违反节点类型的连接约束时连不上,派发 connect-reject,detail 里是两端与全部问题。选中连线后,两端的控制柄拖到别的端口即改连;中段的控制柄拖出拐点,拐点可以拖动,双击去掉;「清除拐点」把连线交回自动路由。拖动途中按 Escape 取消。

平移与缩放与查看器一样:在空白处拖动或滚动滚轮平移,按住 Ctrl 或 ⌘ 滚动滚轮缩放;没有选中节点时方向键平移视口。工具栏上的「拖动」(H)打开以后,在画布任意处按住拖动都是平移,节点上也一样,不选中、不挪节点,光标是手形;再点一下或按 Esc 退出,状态在外框的 canvasTool 上。小地图上按下或拖动,视口中心移到那里。

属性面板 ​

属性面板编辑选中的对象:节点按节点类型的属性描述列出字段;连线列出连线通用的属性,再加上源节点类型给出线登记的属性。选中多个对象时只编辑它们共同的属性,一次写进全部对象;什么都没选中时编辑流程定义级的属性。

字段按属性描述的分组排列。propertyGroupOrder 定分组的先后,propertyGroupLabels 给分组换标题;示例里两项都来自预设的中文文案。在同一个字段里连续输入合成一步撤销,离开字段后再改另起一步。字段的校验问题直接显示在字段上。

问题列表 ​

流程定义、扩展或校验选项变了之后,设计器重新校验,问题列表显示全部问题,同时派发 issues-change。错误与警告的条数在问题列表的面板头上,点一下只看或不看这一级。有问题的节点带 data-issue,取值是落在它上面最严重的一级(error 或 warning),皮肤据此画出标记。点一条问题,设计器选中问题所在的节点或连线并显露它;问题落在某个属性上时,焦点直接交给属性面板里的那个字段。

问题码与说明见问题码。validateOptions 按问题码改写严重程度,写 off 即不报告:

ts
designer.validateOptions = { severity: { 'node.isolated': 'off' } }

validate() 按同样的扩展与选项立即校验一次,宿主保存前可以用它拦下有错误的定义。

流程结构 ​

流程结构面板按定义里的先后列出节点,每个节点下面是从它出去的连线,缺省收起。没有名称的节点写「种类名 · id」,有名称时副文本是种类名;连线没有名称时写「连线 · id」,副文本是「→ 去向」,连线的业务数据里有条件文字(data.condition,XiHan 工作流预设就这样写条件)时写成「条件 → 去向」。

  • 点一行就在画布上选中它,不在视口里时移过来(节点用 revealNode,连线用 revealEdge);画布上选中的对象在面板里跟着选中,选中一条连线时展开它的出发节点。
  • 多选照文件管理器:按住 Ctrl / ⌘ 点击或按 Ctrl / ⌘ + Space 加选与取消,按住 Shift 点击选一段,节点与连线可以一起选;只把新选上的第一行移进视口,只取消了几行时视口不动。Delete 删掉全部选中的。
  • 指针悬停或键盘落在一行时,画布上标出那个节点或连线,离开时撤掉。
  • 删除钮与 Delete 走设计器的删除;只读时不删。行不能拖动,先后跟着流程定义。
  • 面板上方有搜索框,按名称与副文本过滤。标题与帮助是流程自己的说法,在设计器文案的 outline 里。

命令 ​

菜单栏、工具栏、右键菜单与快捷键执行的都是命令。每条内置命令都带图标,工具栏缺省只显示图标,悬停或聚焦时提示命令的名字与快捷键;撤销、复制、对齐、缩放这些四款设计器都有的命令,文案用 designer-shell 语言包里 xh-designer-commands 那一桶,与其它设计器一致,要改它们的文字时改那一桶;撤销、重做与清空历史本身就是 designer-shell 的共用命令,见撤销与保存。工具栏末组是 designer-shell 内置的「面板」:点开一张菜单,列出物料、属性、问题这些能开关的面板,勾选的显示,收起的面板从这里再打开;宿主不要哪一条,用 commandLayout 排掉。设置、帮助、开始引导与编辑 JSON 在菜单栏里,见下文。内置命令的 id、名称与快捷键见内置命令;只读时会修改流程定义的命令不可用,复制、全选与缩放仍可用。命令都有对应的方法,宿主的按钮直接调用方法即可,比如 undo()、duplicate()、align('left')、autoLayout({ direction: 'LR' });按 id 执行用 runCommand(id),内置命令与扩展命令都认,命令存在且此刻可用才执行,返回是否执行了。

最上一条是菜单栏,下面是工具栏。菜单栏放通用的功能,四张菜单与四款设计器一致:

  • 「文件」:保存(Ctrl / ⌘ + S)、打开文件(有导入器时)、导出 PNG 与 SVG、设置(Ctrl / ⌘ + ,,同 openSettings())。
  • 「编辑」:撤销、重做,剪切、复制、粘贴、删除、创建副本,全选;开发者模式开着时还有编辑 JSON(Ctrl / ⌘ + Shift + J,见编辑 JSON)。
  • 「视图」:放大、缩小、适合窗口、实际大小,拖动,自动布局与「对齐与分布」子菜单,「面板」与「外观」两个子菜单;外观里是工具栏、状态栏、菜单栏三个显示开关(改的是设置)。
  • 「帮助」:开始引导、帮助(在新窗口打开本站的设计器一页,地址是导出的 FLOW_DESIGNER_HELP_URL)、快捷键(打开设置的快捷键页),关于(打开设置的关于页)。

画布有焦点时按 F10 把焦点交给菜单栏,左右键在菜单之间走,下键、Enter 或 Space 展开;再按 F10,或在菜单栏上按 Esc,回到画布。窄屏时菜单栏收成行首一颗「菜单」钮,各张菜单是它的子菜单。菜单栏常开,设置里没有关掉它的开关。

宿主自己的操作(新建、预览、返回列表)写成一个扩展的命令,placement 写 menubar:file 就进「文件」菜单,写了 toolbar 也上工具栏;模板标题写成设计器的子元素 slot="menubar-center",显示在整条菜单栏的正中,放不下时截断加省略号,再窄就藏起;保存状态写成 slot="status-end",显示在状态栏右端、缩放比例的左边,状态栏在设置里关掉时跟着不显示;别的要常显的内容写 slot="menubar-end",显示在菜单栏右边。这样设计器外面不必另起一条操作条。

ts
designer.extensions = [{
  id: 'acme.host',
  commands: [
    { id: 'acme.new', label: '新建', shortcut: ['Mod', 'Alt', 'N'], placement: ['menubar:file'], run: () => location.assign('/flows/new') },
    { id: 'acme.back', label: '返回列表', placement: ['menubar:file', 'toolbar'], run: () => history.back() },
  ],
}]
const title = document.createElement('span')
title.slot = 'menubar-center'
title.textContent = '请假审批 · LEAVE-01'
const saved = document.createElement('span')
saved.slot = 'status-end'
saved.textContent = '已保存'
designer.append(title, saved)

右键菜单按落点给出不同的命令:节点上是剪切、复制、创建副本与删除,选中两个以上节点时加上对齐,三个以上时再加上分布;连线上是删除与清除拐点;空白处是粘贴、全选、自动布局与适合窗口,开发者模式开着时末尾是编辑 JSON。在画布上按 ContextMenu 键或 Shift + F10,按当前的选择打开菜单。右键事件可以取消,取消后内置菜单与浏览器的菜单都不出现,宿主可以在 detail 的 x、y 处弹自己的菜单。

选中节点时,选中框上方浮着一条选中工具条:创建副本、复制与删除,选中几个节点时前面多一个「对齐与分布」菜单(align-menu,点开列出六种对齐与两种分布)。贴顶放不下时翻到下方,拖动途中收起;只选了连线、没有选中或只读时不出现。画布有焦点时按 Alt + F10 把焦点交给它,在工具条里按 Esc 回到画布。

commandLayout 重排工具栏、菜单栏的各张菜单、各右键菜单与选中工具条,键是位置(toolbar,菜单栏是 menubar:file、menubar:edit、menubar:view、menubar:help,右键菜单是 menu:node、menu:edge、menu:blank,选中工具条是 selection:node 与 selection:nodes),值是若干组命令 id;某张菜单或某个选中工具条排空了就不出现。写了某个位置就只显示列出的命令,没写的位置用内置排列;排列里写了不存在的命令 id 会抛 TypeError,不会静默跳过。

ts
designer.commandLayout = {
  'toolbar': [['undo', 'redo'], ['auto-layout'], ['zoom-to-fit', 'zoom-to-actual']],
  'menu:blank': [['paste'], ['auto-layout']],
}

面板是物料(palette)、流程结构(outline)、属性(properties)、问题(issues)、历史记录(history)五张卡片与画布右下角的小地图(minimap),id 在包入口导出的 FLOW_PANELS 里。流程结构停在左栏物料下面;历史记录缺省隐藏,打开后停在右栏属性下面。defaultLayout 是宿主给的缺省布局:用户没存过布局时用它,「恢复默认布局」也回到它;不给时全部显示、各在缺省的位置。形状是 @xihan-ui/designer-shell 的 DesignerLayout,写了的字段整项覆盖缺省,常用的是 hidden(收起哪些面板)与 docks(各栏放哪些面板、谁先谁后)。

ts
designer.defaultLayout = { version: 1, hidden: [FLOW_PANELS.palette, FLOW_PANELS.issues, FLOW_PANELS.minimap] }

底部的状态栏左边写选中的对象:什么都没选中时是流程里有几个节点、几条连线,选中一个节点时是它的类型、位置与尺寸,选中一条连线时是连线名,选中多个时是选了几个;右边是缩放比例,点一下回到 100%。设置里关掉「状态栏」时整条不显示。

物料、属性与问题列表各是一张卡片:面板头写标题,属性面板的副标题是当前编辑的对象,「?」弹出这块面板的说明,收起钮把它收成标题条(双击面板头也行),「更多」菜单里换栏、调序、浮出,「×」隐藏它;按住面板头可以拖到左、右、下另一栏,拖到画布上就浮出成小窗。工具栏与「视图」菜单里的「面板」逐块勾选显隐,另有显示或隐藏所有面板(Ctrl / ⌘ + \)、还原面板显隐(Ctrl / ⌘ + Shift + \)与恢复默认布局;每块面板还有一条开关命令 panel:<面板 id>,可以排进 commandLayout。

布局改了,整份写进设置的 layout.panels(DESIGNER_SETTING_KEYS.layout),和设置对话框里改一条一样派发 settings-change,宿主存下来、下次随 settings 赋回即可;给了 settingsStorageKey 时设计器自己存。宿主要固定布局,用 settingsPolicy:layout.panels 锁住整份,layout.panels.<面板 id> 藏掉或锁定一块面板。

除了命令的快捷键,画布有焦点时还认这些键:Backspace 删除,= 放大,F10 进出菜单栏;设置的「快捷键」页把命令的快捷键与这些键列成一张表。Ctrl / ⌘ + C、X、V 走系统剪贴板:复制出的是一段 JSON 文本,能在同一页面、别的页面或别的标签页的设计器里粘贴;读不出流程内容的文本交给扩展的导入器(见导入与导出),没有导入器接下时不会贴进来。宿主调 paste(内容) 时每次比上一次多错开一格;给了落点 paste(内容, { at }) 时内容整体的中心放到 at(世界坐标,视口中央是 getViewportCenter()),左上角吸附网格,不累加错开。剪贴板的文本格式由 encodeFlowClipboard 与 decodeFlowClipboard 互转。

设置 ​

设置是用户偏好,每人每机一份,不随流程定义保存。openSettings(section?) 打开设置对话框,左边一个分区一页:

  • 「基础」(general):主题(跟随页面、跟随系统、亮色、暗色)、主题色(跟随页面、八种预设、自定义颜色)、状态栏、自动保存、开发者模式,以及分页与渲染两条调试日志(开发者模式开着才能改)。
  • 「语言」(language):跟随页面(缺省)、跟随系统,或在十种语言里选一种,名字是各自的本名(简体中文、繁體中文、English、日本語……)。
  • 「布局」(layout):一张面板表,逐块面板改显隐、位置、先后与收起,表下一颗「恢复默认布局」。
  • 「工具栏」(toolbar):按钮的显示方式(图标、文字、图标和文字,缺省只显示图标)、显示工具栏开关,以及一张按钮表:按缺省排列列出工具栏上的命令,可以勾掉不要的、拖动换先后、跨组挪,表下「恢复默认」。工具栏的这些只走设置,没有单独的属性;用户的改动压在宿主的 commandLayout 之上。
  • 「画布」(flow):自动布局的方向与两种间距、吸附对齐。
  • 扩展登记的分区,排在后面。
  • 「帮助」「快捷键」「关于」三页在最后:帮助页是几段操作说明与「开始引导」「帮助」两颗钮;快捷键页把命令的快捷键与画布认的几个键列成一张表,「通用」里方向键一行写明没有选中节点时平移画布,Esc 一行写明取消正在进行的拖动;画布在空白处直接拖动就平移,所以没有「按住空格拖动」那一行;关于页写设计器的版本(包入口导出的 FLOW_VERSION)与许可证,扩展可以在后面加几行(扩展的 about 字段)。

分区 id 与 flow、内建的 general、language、layout、toolbar、help、shortcuts、about 或别的扩展重名时,给 extensions 赋值就抛 DesignerExtensionError。

ts
export function openCanvasSettings(designer: XhFlowDesigner): void {
  designer.openSettings(FLOW_SETTINGS_SECTION)
}

值是扁平的键值,键是「分区 id.条目 id」,如 general.theme、flow.snap,没写的键取缺省值。宿主给缺省、锁定条目或读值时用常量,不手写键:「画布」分区的 id 与各条目的键是包入口导出的 FLOW_SETTINGS_SECTION 与 FLOW_SETTING_KEYS,「基础」「语言」「布局」「工具栏」分区的键是 @xihan-ui/designer-shell 的 DESIGNER_SETTING_KEYS(如 general.brand、language.locale、toolbar.display、toolbar.layout);以后条目改了 path,常量跟着变,宿主的缺省不会静默失效。settings 赋值即生效;用户在对话框里改了一条,设计器换成新的一份并派发 settings-change,宿主存起来、下次赋回。settingsPolicy 隐藏或锁定条目,键是条目键或分区 id:hidden 不显示,一个分区的条目全藏了就没有这一页;locked 只读,说明换成「由所在系统管理」。

ts
export function wireSettings(designer: XhFlowDesigner, saved: FlowSettings, store: (settings: FlowSettings) => void): void {
  // 宿主自己的缺省压在下面,用户存过的值盖在上面
  designer.settings = { [FLOW_SETTING_KEYS.layoutDirection]: 'LR', ...saved }
  designer.settingsPolicy = { [DESIGNER_SETTING_KEYS.theme]: 'hidden', [DESIGNER_SETTING_KEYS.brand]: 'hidden', [FLOW_SETTING_KEYS.snap]: 'locked' }
  designer.addEventListener('settings-change', (event) => {
    store((event as CustomEvent<{ settings: FlowSettings }>).detail.settings)
  })
}
  • 主题只作用于设计器:主题与主题色写在 <xh-flow-designer> 自己的 data-theme、data-brand 上,页面其余部分不变;设置对话框、菜单这些浮层跟着设计器变。主题色是内置的:跟随页面(缺省),或 UI 的八种预设,或自定义颜色,宿主不用传预设。

  • 语言只作用于设计器:设置里选了语言,<xh-flow-designer> 把这一语言的 UI、designer-shell 与流程语言包作为自己的 xhConfig 交出去,设计器、里面的元素与它弹出的浮层都换成这种语言(日期、分页这些 UI 文案也一样),页面别处不变。跟随系统按浏览器的语言偏好挑最接近的一种,挑不到用英文;语言包第一次用到时才加载。跟随页面时 xhConfig 是空对象,照页面的全局配置。

  • 画布的设置:autoLayout() 没给的方向与间距、工具栏的「自动布局」都取「画布」分区的值;吸附对齐关掉时拖动只吸附网格。小地图是一块面板,显隐跟着布局走。

  • 扩展读设置:命令、面板与扩展从上下文的 settings 读值,各分区没写的条目已经取了缺省值,如 context.settings['flow.snap']。

  • 自动保存:设置里开着自动保存时,有未保存的修改,停手约一秒就把整份流程定义存成这台浏览器里的草稿(localStorage),宿主不参与;设计器变干净(宿主存完调 markSaved(),或撤销回到保存点)时删掉草稿,关掉自动保存不删已有的草稿。宿主给 draftKey(同名 attribute draft-key,通常是流程 id)区分不同流程,不给按页面地址区分。下次宿主装进同一个键的流程、和草稿不一样时,问「发现未保存的草稿(时间)」,恢复会整份换上草稿、记一步撤销,丢弃即删掉草稿。不要这一项时用 settingsPolicy 把 general.autoSave 藏起或锁住。Ctrl / ⌘ + S 照旧派发 save,与自动保存无关。

  • 调试日志:开发者模式与「渲染调试日志」都开着时,每次重画在浏览器控制台记一组「[xh-flow-designer] render · update: 改动的属性 耗时」,导出图片也记一组;流程没有分页,「分页调试日志」开了也没有日志。

独立使用、没有宿主替你存时,给 settingsStorageKey:连上文档时若没赋 settings 就从根所在窗口的 localStorage 读,用户改了设置就写回去;标了 secret 的文本条目(如密钥)不存。读写失败派发 settings-storage-error,设置留在内存里。不给就不读写存储。

ts
export function standalone(designer: XhFlowDesigner): void {
  designer.settingsStorageKey = 'flow-designer:settings'
  designer.addEventListener('settings-storage-error', (event) => {
    console.warn('设置没能读写本机存储,这次改的只在内存里', (event as CustomEvent<{ error: unknown }>).detail.error)
  })
}

新手引导 ​

设计器第一次显示出来、画布铺好之后,自动开始一次新手引导:先讲菜单栏(各张菜单里有什么,F10 进出菜单栏),再一步步高亮工具栏、节点面板、画布、属性面板、问题面板与状态栏,接着是流程自己的三步——连线(指向第一个端口)、自动布局与导出图片(指向工具栏上的钮,并说它们也在「视图」「文件」菜单里),最后一步是设置:高亮「文件」菜单,设置在那里,引导能从「帮助」菜单重新开始。目标看不见的那步自动跳过,比如画布上还没有节点时不讲连线。

窄屏时菜单栏收成一颗「菜单」钮,讲菜单栏与设置的两步都落在这颗钮上。

  • 走完、跳过或关上之后,设置里记下 help.tourDone(@xihan-ui/designer-shell 的 DESIGNER_TOUR_DONE_KEY)并派发 settings-change,以后不再自动开始。宿主替用户存设置时,这个键随设置一起存。
  • 设计器藏着时不开始,等它显示出来再看。宿主不要自动开始时写 autoTour = false(同名 attribute auto-tour="false")。
  • 引导限在设计器的外框里:遮罩只盖外框,高亮框裁在外框里,页面别处照常可读可点。同一页同时只有一个引导:别的设计器的引导开着时不自动开始。
  • startTour() 随时从头开始,同一页里别的引导开着时先收起它,没有能走的步骤时返回 false;「帮助」菜单与设置帮助页里的「开始引导」、start-tour 命令做的是同一件事。
  • 扩展可以登记自己的步骤,见扩展的 tourSteps。

撤销与保存 ​

每次修改记为一步撤销,拖动这类连续操作松手时才记为一步。canUndo、canRedo 表示能不能撤销重做,undo()、redo() 执行。

  • 每一步带说明:如「移动 任务 approve_1」「添加 3 个节点」「修改「名称」」「自动布局」「编辑 JSON」。单个节点写「种类 名称」(没有名称时是 id),几个节点写个数;创建副本与按住 Alt 拖动复制都写「复制」加节点;说明跟着界面语言变。撤销、重做钮的名字带上下一步的说明,如「撤销:移动 任务 approve_1」,按钮上看得见的字仍是「撤销」。
  • 合并:在同一个属性字段里连续输入合成一步;画布有焦点时按方向键微移,同一组选中的节点一秒内连按合成一步,换了选中另起一步。
  • 快捷键:撤销 Ctrl / ⌘ + Z,重做 Ctrl / ⌘ + Shift + Z,另认 Ctrl + Y。
  • 清空历史:命令 clear-history,不上工具栏,在历史记录面板的面板头上;点了先弹确认框,确定后才清空,清空后不能再撤销到之前。
  • 历史记录面板:列出每一步与起点,选一步(或起点)就一次跳过去,之后的步骤留着、可以再重做回来;只读时只能看。撤销、重做、清空与跳转之后,读屏会礼貌地读一句。宿主自己改流程定义时用 execute(operations),一组操作记为一步,操作的写法见流程定义结构。

流程定义每次变化派发 schema-change,detail 里是新的定义、这次的操作与种类:execute 是编辑,undo、redo 是撤销重做,load 是装载了新定义。

ts
designer.addEventListener('schema-change', (event) => {
  const { kind, operations } = (event as FlowDesignerEventMap['schema-change']).detail
  status.textContent = `${KIND_TEXT[kind]}:${operations.map(operation => operation.type).join('、')}`
})

dirty 表示有没有未保存的修改,变化时派发 dirty-change。设计器不自己保存:画布有焦点时按 Ctrl / ⌘ + S,或点「文件」菜单的「保存」(命令 save),只派发 save,宿主保存成功后调用 markSaved() 把当前状态记为已保存。撤销回已保存的那一步时 dirty 也会回到 false。

ts
const saveNow = (): void => {
  // 宿主在这里把 designer.schema 交给后端,保存成功后记为已保存
  designer.markSaved()
  status.textContent = '已保存'
}
const save = button('保存', saveNow)
save.disabled = true
designer.addEventListener('save', saveNow)
designer.addEventListener('dirty-change', (event) => {
  save.disabled = !(event as FlowDesignerEventMap['dirty-change']).detail.dirty
})

编辑 JSON ​

openJsonEditor() 打开一个编辑整份流程定义 JSON 的对话框(也是 edit-json 命令做的事)。edit-json 命令只在设置里开着开发者模式时存在:关着时菜单栏、右键菜单里都没有它,快捷键也不响应;宿主要给用户这个入口,就在 settings 里预先写 general.developer: true,或用 settingsPolicy 锁住它。openJsonEditor() 方法照样能调。对话框:编辑框里写出当前的定义,边改边检查,下面列出问题,底边是格式化、复制、取消与应用。

  • 问题与问题列表同一套:先按读入检查结构,读得了再按当前的扩展与 validateOptions 校验,说明与所在对象的名字与问题列表一致。点一条,光标跳到那个路径在文字里的位置。
  • 文字解析不了、有错误级的问题或与打开时相同时,「应用」不可用。
  • 应用即整份换上,记为一步撤销,派发 schema-change(种类是 execute),选择清空,视口随后按实际大小摆好;撤销一步回到原来那份。
  • 只读时能看、能格式化与复制,不能应用。

导出图片 ​

export("svg" | "png", options) 与查看器的导出图片相同,选项也一样;选中的节点与连线按没选中的样子画。它是设计器自带的图片导出器,与扩展登记的导出器走同一个 export()。

工具栏最后一组是「导出 PNG」与「导出 SVG」,流程图是空的时不可用,只读时照样能用。导出后派发可以取消的 export 事件,detail 里是格式与文件;没被取消时存成 flow.png 或 flow.svg 下载,宿主想自己命名或上传就取消它,拿 detail.blob 自己存。不想要这两个按钮时用 commandLayout 把它们去掉。

只读 ​

readonly 打开后仍可以选择、浏览、复制与缩放,不能修改:拖动、连线、删除、粘贴与属性面板的编辑都不起作用,会修改的方法什么都不做。元素上带 data-readonly。

同名 attribute 是三态的,与 XiHan.UI 的元素一致:不写为缺省(不只读);写 readonly="false" 为 false;写 readonly、readonly="true" 或别的值为 true;摘掉 attribute 回到缺省。这是行为变化:以前只要写了这个 attribute 就算只读,readonly="false" 也不例外。经 property 赋值不受影响。

ts
const readonly = button('只读', () => {
  designer.readonly = !designer.readonly
  readonly.setAttribute('aria-pressed', String(designer.readonly))
})
readonly.setAttribute('aria-pressed', 'false')

文案 ​

设计器的缺省文案是英文。全站换语言时与查看器一样合并同一语言的流程语言包(如 flowZhCN、flowJaJP),写法见快速开始。单个实例要改时写 translations,它压在全局配置之上;可以改的文案见 FlowDesignerTranslations。

Released under The MIT License