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

查看生态总览
跳转到内容

设计器 ​

<xh-form-designer> 是可视化的表单设计器:物料、画布、属性与问题列表都在里面,可以切到预览看真实效果。它只吃模板、吐模板与事件,模板存在哪里、谁能改,都由宿主决定。全部属性、方法与事件见设计器参考。

接入 ​

bash
pnpm add @xihan-form/designer @xihan-form/runtime @xihan-ui/styles @xihan-ui/designer-shell
ts
import type { FormDocument } from '@xihan-form/core'
import type { FormDesignerEventMap } from '@xihan-form/designer'
import { defineFormDesigner, FORM_TOOLBAR_LAYOUT } from '@xihan-form/designer'
import { schema, values } from '../templates/travel'
import '@xihan-ui/styles/index.css'
import '@xihan-ui/designer-shell/style.css'
import '@xihan-form/runtime/style.css'
import '@xihan-form/designer/style.css'

defineFormDesigner()

defineFormDesigner() 注册设计器以及它用到的全部元素,包括画布与预览共用的渲染器。样式要引入四份:XiHan.UI 的皮肤、设计器零件、渲染器与设计器自己的样式表。设计器要有高度,由宿主的布局给出。

ts
const designer = document.createElement('xh-form-designer')
designer.schema = schema
designer.previewValues = values
designer.commandLayout = { toolbar: [...FORM_TOOLBAR_LAYOUT, ['save']] }

schema 是正在编辑的模板,读它就是当前的模板;给 schema 赋一个新模板会清空撤销历史与选择。

界面 ​

设计器的外框从上到下是菜单栏、工具栏、中间一行与状态栏。中间一行左边是物料与页面结构,中间是画布,右边是属性,画布下方是问题列表。

区域内容
菜单栏文件、编辑、视图、帮助四张菜单;正中放宿主的模板标题,右边一段放宿主别的常显内容,见菜单栏
工具栏撤销、重做;删除、创建副本;设计与预览;拖动与预览宽度三档;末组是面板
物料(左)卡片。按分类列出可以放进表单的种类:每组一行组名,下面是方形小卡片,图标在上、种类名在下,按面板宽度排成几列,悬停显示全名;全部展开,可搜索。拖到画布上放进想要的位置,点一下插到选中节点之后,没有选中时放到末尾。扩展可以经物料来源在后面加几组
页面结构(左,物料下面)卡片。按树列出模板里的节点,见页面结构
画布(中)按模板排出的表单,所见即所得;只选中一个节点时它的右下角带一条选中工具条:选中上一级、上移、下移、创建副本、删除
历史记录(右,属性下面)卡片,缺省隐藏,从工具栏或「视图」菜单的「面板」打开。按先后列出撤销历史的每一步,见撤销与历史记录
属性(右)卡片。副标题写当前编辑的对象,面板头下面一排三个页签:属性、校验规则、联动规则。没有选中时编辑表单设置,选中区域时编辑区域属性
问题(画布下方)卡片。模板自检出的问题,面板头上是错误数与警告数,兼作筛选开关;点一条就选中并滚到出问题的节点
状态栏左边写选中的字段种类与字段名,选中区域写它所在的容器,多选写个数,什么都没选写字段数;右边是预览宽度,点一下回到全宽;宿主的保存状态排在预览宽度左边

预览态的外框不显示面板,只留菜单栏、工具栏与状态栏,画布换成渲染器,见预览。

卡片与面板 ​

物料、页面结构、属性、历史记录与问题列表各是一块面板,画成一张卡片:面板头写图标与标题,「?」弹出这块面板的说明,右边依次是收起、「更多」与关闭。面板停在左、右、下三栏,缺省物料与页面结构在左栏(页面结构在物料下面)、属性与历史记录在右栏(历史记录缺省隐藏)、问题在下栏;可以换栏、调先后、收成标题条,也可以浮出成小窗再吸回:

  • 按住面板头拖到另一栏或两块面板之间就停在那里,拖到画布上就地浮出;不用拖的话,「更多」菜单里有移到左栏、右栏、下栏,上移、下移,浮出与停靠,键盘与读屏也靠它。
  • 「×」隐藏这块面板,工具栏或「视图」菜单的「面板」里勾回来再显示。菜单里还有「显示 / 隐藏所有面板」(Ctrl / ⌘ + \)、「还原面板显隐」(Ctrl / ⌘ + Shift + \)与「恢复默认布局」。
  • 每块面板另有一条开关命令 panel:<面板 id>,可以排进工具栏、菜单栏或右键菜单。

挪了面板、改了显隐或大小,设计器把整份布局写进设置的 layout.panels(DESIGNER_SETTING_KEYS.layout),并像改了别的设置一样派发 settings-change,宿主存下来、下次随 settings 赋回即可,见设置。宿主要换掉内置的缺省布局时给 defaultLayout:用户还没存过布局时用它,「恢复默认布局」也回到它,写了的字段整项覆盖内置缺省。面板 id 用包入口导出的 FORM_DESIGNER_PANELS(palette、outline、properties、issues、history)。settingsPolicy 也管布局:layout.panels.<面板 id> 写 hidden 就没有这块面板,写 locked 就固定它;layout.panels 锁定整份布局。

ts
export function hostLayout(designer: XhFormDesigner): void {
  // 缺省收起问题面板;物料面板固定在原处,不能挪动或隐藏
  designer.defaultLayout = { version: 1, hidden: [FORM_DESIGNER_PANELS.issues] }
  designer.settingsPolicy = { [`${DESIGNER_SETTING_KEYS.layout}.${FORM_DESIGNER_PANELS.palette}`]: 'locked' }
}

页面结构 ​

页面结构按树列出模板里的节点,像图层面板一样找东西、删东西、挪东西:

  • 顶层是模板的节点,容器下面按区域分组:栅格写「第几列」,表格布局写「第几行第几列」,标签页与折叠项写标题(没写标题时写「区域几」);卡片、分组、子表单、子表只有一个区域,子节点直接挂在容器下。表格布局只列有内容的格。
  • 节点带种类的图标,写「种类 · 数据键」,下面一行小字是它的标签或标题。上面有搜索框,按名字与小字过滤。
  • 点一行就在画布上选中它并滚过去,节点或区域在没切到的标签页、收着的折叠项里时先切过去、展开;画布上的选择变了,树上跟着变。悬停或用方向键走到一行时,画布上用虚线框标出它。
  • 能多选,与画布上的选择是同一份:按住 Ctrl(macOS 为 ⌘)点一行或按 Ctrl / ⌘ + Space 加选、取消,按住 Shift 点一行从上一次点的那行(画布上刚选过的话从画布上最后选的那个)选到这一行。节点与区域不能同时选:新选上的有节点就只留节点;只新选上了区域时,只留和它同一个容器的区域。新选上的第一个在画布上滚进可见区。焦点在选中的行上时,Delete 删掉全部选中的行。
  • Delete 或行尾的删除钮删掉这一行,与工具栏的删除同一条路,区域照删区域的规矩(表格布局的格不删,删完不少于容器的下限)。
  • 拖动一行或按 Alt + 方向键挪动节点,放得下才许放(子表只收字段,不能放进自己里面),一次挪动记为一步撤销。区域行不能挪。
  • 只读时能选、能标出,不能删、不能挪;预览态不显示这块面板。

扩展登记的面板给了标题也画成卡片,同样能挪动、浮出与隐藏,见扩展。画布总在。状态栏只受设置里的「状态栏」开关控制。

菜单栏 ​

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

  • 「文件」:保存(Ctrl / ⌘ + S,派发 save,见保存)、设置(Ctrl / ⌘ + ,,同 openSettings())。
  • 「编辑」:撤销、重做,剪切、复制、粘贴、删除、创建副本,全选,编辑 JSON(Ctrl / ⌘ + Shift + J,开发者模式开着时才有,见编辑 JSON)。
  • 「视图」:设计、预览,拖动,预览宽度三档,「面板」与「外观」两个子菜单:面板里逐块面板一项,勾上的显示;外观里是工具栏、状态栏两个显示开关(改的是设置)。
  • 「帮助」:开始引导、帮助(在新窗口打开本站的这一页,地址是包入口导出的 FORM_DESIGNER_HELP_URL)、快捷键(打开设置的快捷键页),关于(打开设置的关于页)。

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

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

ts
export function hostMenubar(name: string): void {
  designer.extensions = [{
    id: 'acme.host',
    commands: [
      { id: 'acme.new', label: '新建', placement: ['menubar:file'], run: () => location.assign('/forms/new') },
      { id: 'acme.back', label: '返回列表', placement: ['menubar:file', 'toolbar'], run: () => history.back() },
    ],
  }]
  const title = document.createElement('span')
  title.slot = 'menubar-center'
  title.textContent = name
  const saved = document.createElement('span')
  saved.slot = 'status-end'
  saved.textContent = '已保存'
  designer.append(title, saved)
}

工具栏 ​

工具栏缺省只显示图标,设置「工具栏」页的「按钮显示方式」可以换成只显示文字或图标加文字,那一页还能隐藏整条工具栏、调按钮的先后与分组、藏起不用的按钮(见设置)。按钮的名字与快捷键报给读屏,悬停或键盘聚焦到按钮时提示命令的文字与快捷键;会删掉内容的命令按危险色显示。宽度不够时,放不下的按钮收进末尾的「更多」菜单。

末组是 designer-shell 内置的「面板」:点开一张菜单,列出能开关的面板,勾选的显示,后面是全部显隐、还原显隐与恢复默认布局。设置与帮助在菜单栏里。宿主不要哪一条,用 commandLayout 排掉,见工具栏与菜单的排列。

撤销、重做、清空历史、剪切、复制、粘贴、创建副本、删除、全选、设计与预览,以及保存、面板、拖动、设置、快捷键、关于、帮助、外观与两个显示开关,这些四款设计器共用的命令,文字取 designer-shell 语言包的 xh-designer-commands,菜单栏四张菜单的名字、工具栏与菜单栏的可访问名也在这里;各语言的 form… 语言包已经带上。要换这些说法就改这一桶,设计器的 translations 属性只覆盖表单自己的文案。

编辑 ​

放进节点:把物料拖到画布上的位置,或点物料条目,插到选中节点之后,没有选中时放到末尾。区域只收它能放的节点,如子表的区域只收字段;放不下的位置拖过去时不给落点。

选择:点节点选中它,按住 Shift、Ctrl 或 ⌘ 再点是追加或取消。也可以选中容器里的区域:栅格的一列、标签页的一页、折叠面板的一项、表格布局的单元格。

移动:把画布上的节点拖到新位置,或用选中工具条、右键菜单与快捷键上移下移。

拖动画布:工具栏上的「拖动」(H)打开以后,在画布任意处按住拖动都是滚动画布,节点与跨度把手上也一样,不选中、不挪节点,点一下也不改选择,光标是手形,拖动中握拳;只读与预览态照样能拖。拖动途中按 Esc 停在此处,拖动工具还开着;再点一下「拖动」或按 Esc 退出,状态在外框的 canvasTool 上。

右键菜单按选中的东西给出命令:节点有剪切、复制、粘贴、创建副本、选中上一级、上移、下移与删除;表格布局的单元格另有合并、拆分、插入与删除行列;画布空白处有粘贴,开发者模式开着时还有编辑 JSON。菜单键或 Shift + F10 也能打开。

复制粘贴走系统剪贴板:复制的节点以表单自己的格式与 JSON 文本两种写进剪贴板,可以粘贴到另一个设计器、另一个页签,也可以粘贴一段 JSON 文本。粘贴时 id 全部换新,与目标作用域冲突的数据键改成新的键;剪贴板里的内容不是合法的表单节点时不粘贴。

快捷键分两类。带 Ctrl / ⌘ 的在整个设计器里都认,焦点在物料、属性或问题列表里也一样;焦点在输入框、文本框这些能输入文字的地方时,这些键归输入框自己(比如撤销的是输入框里的字),面板里的组件自己处理了的键也不再当快捷键。复制、剪切在画布外选着一段文字时,复制的是那段文字。

按键作用
Ctrl / ⌘ + Z撤销
Ctrl / ⌘ + Shift + Z,Ctrl + Y重做
Ctrl / ⌘ + C、X、V复制、剪切、粘贴
Ctrl / ⌘ + D创建副本:复制并紧跟着粘贴
Ctrl / ⌘ + A全选
Ctrl / ⌘ + S保存:派发 save
Ctrl / ⌘ + ,打开设置
Ctrl / ⌘ + \显示或隐藏所有面板
Ctrl / ⌘ + Shift + \还原面板显隐
Ctrl / ⌘ + Shift + J打开 JSON 编辑框(开发者模式开着时)

F10 不论焦点在画布、面板还是工具栏上都认:把焦点交给菜单栏,再按一次回到原来的地方。

不带 Ctrl / ⌘ 的单键只在焦点在画布里时认,物料与属性面板里的列表、开关照常用这些键:

按键作用
Delete,Backspace删除选中的节点或区域
↑ ↓在同一层里移动选择
Alt + ↑ ↓上移、下移选中的节点,连续按的并成一步撤销
Enter选中第一个子节点
H打开或关闭拖动工具
Esc选中上一级,再按一次清空选择;拖动工具开着时先退出拖动,选择不动
菜单键,Shift + F10打开右键菜单

属性、校验与联动 ​

属性面板按选中的节点列出它的属性,分基础、数据、外观三组,每个属性用合适的编辑方式:文本、数字、开关、下拉、选项表等。多个节点同时选中时只列出它们共有的属性,取值不同的显示「多个值」,改了就写给每个节点。

下拉、单选组等字段的选项在「数据」组的选项表里编辑:一项一行,三格依次是值、文字与禁用,可以新增、删除、上移、下移,键盘都能操作。值为空、值重复或缺了文字时,模板自检报出问题,问题列表里列出这一条,选项表把它标在那一项的那一格上。

树选择、级联的选项是一棵树,在「数据」组的选项树里编辑:一项一行,下一层缩进排在上一层下面,四格依次是值、文字、禁用与操作。操作格里可以给这一项加子项或删掉它(连同下一层),表格下面的「新增一项」加在顶层末尾。键盘上方向键在格子之间走,Enter 或 F2 进格内编辑、Esc 回到格子;Alt + 上下键在同一层换序,树里已有层级时 Alt + 左右键改一层缩进,也可以按住行拖动。同一层里值重复、值为空或缺了文字时,问题同样标在那一项的那一格上。

「校验规则」与「联动规则」两个页签编辑节点的规则:新增、删除、调整顺序、改每条规则的字段。条件、取值与表达式规则用表达式输入框,边输入边检查,并补全数据键、$value 等变量与函数名。规则的含义见校验与联动。

撤销与历史记录 ​

改动随输入写进模板,连续改同一个属性合成一步撤销。拖栅格一列的跨度从按下到松手是一步,拖动中按 Esc 取消、跨度回到原样;连续按 Alt + ↑ ↓ 挪同一个节点,相隔一秒以内的并成一步,换了选择就另起一步。

撤销历史的每一步带一句说明,写成「动作 + 对象名」,如「删除「单行文本 · name」」「修改「标签」」「移动 2 个组件」,跟着设计器的语言变。工具栏上撤销、重做的全名带上下一步的说明,如「撤销:删除「单行文本 · name」」,按钮上看得见的仍是图标与「撤销」。直接用 createFormEditor 的宿主把 describeFormOperations 传给它的 describe,也能得到同样的说明。

历史记录面板按先后列出每一步:第一行是打开时(步数超过上限丢掉过最早的几步时写「更早的步骤已丢弃」),当前位置那一行选中,已撤销的步骤字色减淡,保存时所在的那一步标「已保存」。选一行就跳到那一步,一次撤销或重做到位,只派发一次 schema-change;跳回去以后再改,后面已撤销的步骤照常丢掉。只读时只能看。

清空历史(clear-history)丢掉全部撤销与重做的步骤,模板不变;它在历史记录面板的面板头上,不上工具栏,执行时先弹确认框,也可以经 commandLayout 排进工具栏或右键菜单。只读时撤销、重做与清空历史都不可用,undo()、redo() 返回 false。

宿主也能读写撤销历史:historyState 是此刻的快照(各步的说明、位置号、当前位置与起点),goToHistory(position) 一次跳到某一步或起点(只读、预览或拖动中返回 false),clearHistory() 清空撤销与重做;历史有任何变化(执行、撤销、重做、跳转、装载、清空、保存)都派发 history-change,载荷就是 historyState。撤销步数上限是 historyLimit(同名特性 history-limit),缺省 100,下一次给 schema 赋值时生效。

编辑 JSON ​

「编辑」菜单或画布空白处右键菜单里的「编辑 JSON」、快捷键 Ctrl / ⌘ + Shift + J 或 openJsonEditor() 打开 JSON 编辑框,写出此刻的整份模板,缩进两格。它是调试入口,菜单、快捷键与命令只在设置「基础」里的开发者模式开着时才有;openJsonEditor() 方法照样能调。宿主要让用户用它,就在 settings 里预先写 'general.developer': true,或用 settingsPolicy 锁住 general.developer。

编辑框里:

  • 每改一次都重新解析,并按 validate() 同一套检查:扩展登记的种类与规则都认,给了 expressionEngine 时也查表达式。问题列在编辑框下面,说明与问题列表一样;点一条,光标跳到它在文字里的位置。解析不了或有错误级问题时「应用」不可用。
  • 按「应用」整份换上,作为一步进撤销栈,历史里写「编辑 JSON」,撤销就回到原来那一份;派发的 schema-change 里是一条 replaceDocument 操作。
  • 只读或预览态时只能看、格式化与复制,不能应用。
  • 命令 id 是 edit-json,开发者模式开着时缺省在「编辑」菜单里、不上工具栏,要放就写进 commandLayout;关着时它不存在,写进排列也不显示。

问题列表 ​

设计器随改随做模板自检,问题列在问题面板里,也随 issues-change 派发给宿主;问题码见问题码。需要时宿主也可以调用 validate() 取一次。

预览 ​

工具栏或「视图」菜单的「预览」,或 mode = 'preview' 切到预览态:画布换成真实的渲染器,可以填写、校验、看联动,外框不显示面板,菜单栏与工具栏照常在,从那里切回设计态时面板照原来的布局回来。previewValues 是预览用的示例数据,loadOptions 是预览时的选项加载函数,uploadFile 是预览里文件字段的上传函数,都与渲染器的同名属性一样;不给 uploadFile 时,预览里选的文件不会上传,也不会写进字段值。

预览宽度有全宽、平板与手机三档(previewWidth),设计态的画布也按这个宽度排。

设置 ​

设置是用户偏好,每人每机一份,不随模板保存。「文件」菜单的「设置」(Ctrl / ⌘ + ,)或 openSettings(section?) 打开设置对话框,左边一个分区一页:

  • 「基础」(general):主题(跟随页面、跟随系统、亮色、暗色)、主题色(跟随页面、八种预设、自定义颜色)、状态栏、开发者模式(general.developer,缺省关;关着时编辑 JSON 这类调试入口都不出现),分页调试日志与渲染调试日志两个开关(开发者模式开着时才能改,日志见下文),以及自动保存(general.autoSave,缺省关,见保存)。
  • 「语言」(language):跟随页面(缺省)、跟随系统,或在十种语言里选一种,名字是各自的本名(简体中文、繁體中文、English、日本語……)。
  • 「布局」(layout):一张面板表,逐块面板一行,显示开关、停在哪一栏(或浮动)、上移下移与收成标题条,表下「恢复默认布局」;和面板头的「更多」菜单一样,不靠拖动也能改布局,改的就是 layout.panels(见卡片与面板)。
  • 「工具栏」(toolbar):按钮显示方式(toolbar.display)、显示工具栏(toolbar.visible),以及一张按钮表(toolbar.layout):一组一块,组头三态勾选框,每个按钮一行,可以勾掉、拖动换先后或挪到别的组,表下「恢复默认」。只管顶部工具栏:右键菜单与选中工具条不在这里改,由宿主的 commandLayout 排。
  • 「帮助」(help):表单的几段说明,以及「开始引导」「打开文档」两颗钮;点了先关上对话框,再跑对应的命令(start-tour、help)。
  • 「快捷键」(shortcuts):按命令表列出,分通用、编辑、本设计器与扩展几组,同一条命令的第二个快捷键写在同一行,保存、设置这些外壳命令在「通用」里。「本设计器」那组末尾是表单不是命令的按键:↑ ↓ 选上一个、下一个,Alt + ↑ ↓ 上移、下移,Enter 选中里面第一个,Esc 选中上一级,Shift + F10 打开右键菜单。「通用」组末尾只列表单有的三个按键:F10(进出菜单栏)、Alt + F10(焦点在画布里时,焦点移到选中工具条)与 Esc;表单的画布不能微移,也不能按住空格拖动。
  • 「关于」(about):产品名、版本(包入口导出的 FORM_DESIGNER_VERSION)、界面库版本与许可证,后面接各扩展给的几行(见扩展)。
  • 扩展登记的分区按各自的 order 排:基础、语言、布局、工具栏是 0–3,帮助、快捷键、关于是 900–920,没写 order 的是 0。分区 id 与内置的或别的扩展重名时,给 extensions 赋值就抛 DesignerExtensionError。

表单没有自己的设置分区:预览宽度是正在看的视图,不是偏好,仍是 previewWidth 与工具栏、「视图」菜单里的三档。

ts
export function openGeneralSettings(designer: XhFormDesigner): void {
  designer.openSettings('general')
}

值是扁平的键值,键是「分区 id.条目 id」,如 general.theme、language.locale,没写的键取缺省值;基础、语言、布局、工具栏几个分区的键用 designer-shell 导出的 DESIGNER_SETTING_KEYS,不手写字符串。settings 赋值即生效;用户在对话框里改了一条,设计器换成新的一份并派发 settings-change,宿主存起来、下次赋回。settingsPolicy 隐藏或锁定条目,键是条目键或分区 id:hidden 不显示,一个分区的条目全藏了就没有这一页;locked 只读,说明换成「由所在系统管理」。

ts
export function wireSettings(designer: XhFormDesigner, saved: FormSettings, store: (settings: FormSettings) => void): void {
  designer.settings = saved
  designer.settingsPolicy = { [DESIGNER_SETTING_KEYS.theme]: 'hidden', [DESIGNER_SETTING_KEYS.brand]: 'hidden' }
  designer.addEventListener('settings-change', (event) => {
    store((event as CustomEvent<{ settings: FormSettings }>).detail.settings)
  })
}
  • 主题只作用于设计器:主题与主题色写在 <xh-form-designer> 自己的 data-theme、data-brand 上,页面其余部分不变;设置对话框、菜单这些浮层跟着设计器变。主题色是内置的:跟随页面(缺省),或 UI 的八种预设,或自定义颜色,宿主不用传预设。
  • 语言只作用于设计器:设置里选了语言,<xh-form-designer> 把这一语言的 UI、designer-shell 与表单语言包作为自己的 xhConfig 交出去,设计器、里面的元素(含预览里的渲染器与日期选择这些 UI 元素)与它弹出的浮层都换成这种语言,页面别处不变。跟随系统按浏览器的语言偏好挑最接近的一种,挑不到用英文;语言包第一次用到时才加载。跟随页面时 xhConfig 是空对象,照页面的全局配置。
  • 工具栏:按钮显示方式有只显示图标(缺省)、只显示文字、图标加文字,只显示图标时按钮的名字照样报给读屏,悬停或聚焦时提示文字。关掉「显示工具栏」只是不显示那一条,命令照样能经快捷键与右键菜单执行。按钮表的缺省排列是宿主 commandLayout 的 toolbar(没写时是 FORM_TOOLBAR_LAYOUT)再追加扩展的那一组,用户的定制套在它上面:后装的扩展命令放到缺省排列里挨着的位置,已经没有的命令跳过。宿主不让用户改时用 settingsPolicy 锁定或藏掉 toolbar.layout。
  • 调试日志:只写到浏览器的控制台,开发者模式与这一路都开着时才记。渲染日志每次重画记一组「[xh-form-designer] render · update: 改动的属性 耗时」,重铺画布(canvas,带节点数)与模板自检(validate,带问题数)各记一组;表单没有分页,分页日志开着也没有内容。关掉开发者模式,两路日志随即停。
  • 扩展读设置:命令、面板与扩展从上下文的 settings 读值,各分区没写的条目已经取了缺省值,如 context.settings['acme.dense']。

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

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

新手引导 ​

第一次打开设计器时,画布铺好后自动开始一遍新手引导,一步高亮一处:欢迎之后先是菜单栏(各张菜单里有什么、F10 进出菜单栏),再是工具栏、物料、画布、属性、问题列表与状态栏,然后是表单自己的三步——页面结构、属性面板的「校验规则」「联动规则」两页,以及工具栏的预览(说明里带一句「视图」菜单里也能切换设计与预览、换预览宽度),最后是设置:高亮「文件」菜单的入口,说设置在那里、引导能从「帮助」菜单重新开始。目标面板隐藏、工具栏上没有那条命令时那一步跳过;只读时不讲校验联动那一步。进入一步前,设计器把那块面板亮出来:展开收起的栏或标题条,窄屏时打开抽屉。

窄屏时菜单栏收成一颗「菜单」钮:菜单栏一步高亮那一条,设置一步落在「菜单」钮上。

  • 走完、跳过(含按 Esc)或关掉时,设计器派发 tour-end:reason 是 complete、skip 或 close,step 是收起时那一步的 id。同时在设置里记下 help.tourDone,照常派发 settings-change;宿主把设置存好,下次就不再自动开始。
  • 引导只铺在设计器的外框里:遮罩与高亮都不出外框,页面别处照常可用。同一页同时只开一个引导:别的设计器的引导开着时,自动开始让开;startTour() 与「开始引导」先收起那一个再开始。
  • autoTour 写 false(特性写 auto-tour="false")就不自动开始。startTour() 随时从头开始,没有能走的步骤时返回 false。「帮助」菜单与设置对话框「帮助」页的「开始引导」、命令 start-tour 做同一件事;命令缺省不上工具栏,要放就写进 commandLayout。
  • 扩展用 tourSteps 加自己的步骤,见扩展。

本站的示例都关掉了自动开始。

保存 ​

设计器不存模板,只告诉宿主什么时候变了、用户想什么时候存:

  • 每次修改派发 schema-change,载荷里是变化后的模板与这一步的操作,可以用来自动保存。拖动跨度这样的手势在松手时派发一次,操作是整个手势的;取消的手势不派发。
  • 有没有未保存的修改变了时派发 dirty-change。
  • 用户按 Ctrl / ⌘ + S 或点「文件」菜单的「保存」(designer-shell 内置的命令 save)时派发 save;宿主存好后调用 markSaved(),设计器把当前模板记为已保存。
ts
let saved: FormDocument = schema
designer.addEventListener('dirty-change', (event) => {
  status.textContent = (event as FormDesignerEventMap['dirty-change']).detail.dirty ? '有未保存的修改' : `已保存,共 ${saved.children.length} 个顶层节点`
})
designer.addEventListener('save', () => {
  // 这里由宿主把模板存进数据库或文件
  saved = designer.schema
  designer.markSaved()
})

设置「基础」里开着自动保存时,设计器把未保存的修改存成这台浏览器里的草稿:停手约一秒写一次,连着改只写最后一次;存好(markSaved())或撤销回已保存的那一步时删掉。下次宿主给 schema 装进同一个草稿键的模板、和草稿不一样时,设计器弹框问「发现未保存的草稿」:恢复就整份换成草稿、记一步撤销,丢弃就删掉;一样就悄悄删掉,只读时不问。草稿键是 draftKey(同名特性 draft-key),宿主写模板 id,不写时按页面地址区分。草稿只在本机,宿主不用存、也不用接新事件;不想让用户开,就用 settingsPolicy 藏起或锁住 general.autoSave。

readonly 让设计器只能选择、浏览与预览,不能修改。写成特性(attribute)时按三态读:没写或写 "false" 可修改,空串、"true" 与别的写法只读,移除后回到可修改。这是一处行为变化:此前 readonly="false" 也算只读,现在是可修改。

工具栏与菜单的排列 ​

commandLayout 按位置写命令 id 的分组:toolbar 是工具栏,menubar:file、menubar:edit、menubar:view、menubar:help 是菜单栏的四张菜单(扩展登记的菜单是 menubar:<菜单 id>),menu:node、menu:region、menu:cell、menu:blank 是选中节点、区域、表格单元格与空白处的右键菜单,selection:node 是只选中一个节点时浮在它右下角的选中工具条。写了某个位置就只显示列出的命令;没写的位置用内置的排列 FORM_TOOLBAR_LAYOUT、FORM_MENU_LAYOUTS 与 FORM_SELECTION_LAYOUTS,菜单栏用 designer-shell 的缺省排列(预览宽度三档经 placement 落在「视图」菜单),再把登记在这个位置的扩展命令追加成一组(菜单栏里排在那张菜单收尾的一组之前)。写成 { 'selection:node': [] } 就没有选中工具条,{ 'menubar:help': [] } 就没有「帮助」菜单。示例在内置的工具栏末尾加了保存:

ts
designer.commandLayout = { toolbar: [...FORM_TOOLBAR_LAYOUT, ['save']] }

内置命令的 id:undo、redo、clear-history、copy、cut、paste、duplicate、delete、select-all、select-parent、move-up、move-down、merge-cells、split-cell、insert-row-above、insert-row-below、insert-column-left、insert-column-right、remove-row、remove-column、design、preview、width-full、width-tablet、width-phone,以及 designer-shell 内置的 save、panels、panels-toggle-all、panels-restore、layout-reset、pan-tool、settings、shortcuts、about、help、start-tour、edit-json、appearance(外观子菜单)、show-toolbar、show-status-bar 与各面板的开关命令 panel:palette、panel:outline、panel:properties、panel:history、panel:issues。扩展可以登记自己的命令,见扩展。

示例 ​

出差申请:菜单栏正中是模板标题;点物料加一个字段,状态栏右端的保存状态显示有未保存的修改;点一下画布再按 Ctrl / ⌘ + S,或点「文件」菜单、工具栏的保存,状态显示已保存。

Released under The MIT License