MaskedTextEdit 固定格式输入
通用布局能力:
RenderMaskedTextEdit支持width、height、min/max、margin和槽位对齐;fieldHeight只控制输入主体高度。详见组件通用布局属性。
RenderMaskedTextEdit 用于录入结构固定、但不参与数值运算的文本,例如病案号、固定长度编号、电话号码和自定义业务编码。
它在 RenderTextBox 的输入、选区、粘贴和 IME 会话之上增加 MaskEngine。业务保存的是不含分隔符的 raw value,界面绘制的是包含固定字符的 display value。
API 总览
主类:
RenderMaskedTextEditMaskEngine
相关 public type:
RenderMaskedTextEditOptionsMaskTokenDefinitionMaskTokenDefinitionsMaskValueMaskEditResult
导入:
import { MaskEngine, RenderMaskedTextEdit, type MaskTokenDefinitions, type MaskValue,} from 'ds-ui'
何时使用
适合:
- 病案号、就诊号、设备编号等固定格式字符串。
- 电话号码、邮政编码等需要自动插入分隔符的字段。
- 只允许数字、字母或字母数字组合的业务编码。
- 需要业务自定义字符规则和大小写转换的输入。
不适合:
- 普通自由文本,使用 TextField。
- 需要数值运算、范围和步进的字段,使用 NumberInput。
- 日期、时间和持续时长,分别使用 DatePicker、TimeEdit 和 TimeSpanEdit。
- 格式会根据上下文大幅变化,或者需要在同一字段中任意跳过中间段的输入。这类规则应由专用编辑器承担。
最小示例
import { RenderMaskedTextEdit } from 'ds-ui' const patientState = { phone: '',} const phone = new RenderMaskedTextEdit({ mask: '000-0000-0000', value: patientState.phone, placeholder: '输入手机号', clearable: true, onChange: (value, details) => { patientState.phone = value console.log(details.display, details.complete) },})
输入 13800138000 后:
phone.value // '13800138000'
phone.displayValue // '138-0013-8000'
phone.complete // true
值模型
组件同时维护两种值:
| 值 | 示例 | 用途 |
|---|---|---|
| raw value | '13800138000' |
value、业务状态和回调的第一参数,不包含 mask 固定字符。 |
| display value | '138-0013-8000' |
displayValue 和界面文本,包含自动插入的固定字符。 |
构造参数和运行时属性 value 都接收 raw value。写入时会:
- 按 mask 槽位逐个 Unicode code point 检查。
- 丢弃不符合当前槽位的字符。
- 超过 mask 容量的字符会被截断。
- 重新生成
displayValue和complete。
程序直接写入 edit.value 或调用 reset() 不会触发 onChange。
const code = new RenderMaskedTextEdit({ mask: 'AA-0000', value: 'ab12x34',}) code.value // 'ab1234'code.displayValue // 'ab-1234'
固定字符按输入进度显示,不会为尚未填写的槽位绘制 prompt character。例如 mask 为 000-000 时:
| raw value | display value |
|---|---|
'' |
'' |
'1' |
'1' |
'123' |
'123-' |
'1234' |
'123-4' |
内置 mask token
| Token | 是否必填 | 接受字符 |
|---|---|---|
0 |
是 | 数字。 |
9 |
否 | 数字。 |
L |
是 | Unicode 字母,包括中文等 \p{L} 字符。 |
? |
否 | Unicode 字母。 |
A |
是 | Unicode 字母或数字。 |
a |
否 | Unicode 字母或数字。 |
其他字符按固定文本处理:
const dateLikeCode = new RenderMaskedTextEdit({ mask: '0000-00-00',})
这里的两个 - 由组件自动插入,不属于 raw value。
转义 token
反斜杠 \ 会把下一个字符转成固定文本。因为 TypeScript 字符串本身也使用反斜杠转义,代码中通常需要写成 \\:
const prefixedCode = new RenderMaskedTextEdit({ // 界面格式:A-BF;第一个 A 是固定字符,两个 X 是自定义槽位。 mask: '\\A-XX', definitions: { X: { pattern: /[A-F]/i, transform: character => character.toUpperCase(), }, }, value: 'bf',})
如果希望数字 0、字母 A 等内置 token 作为固定字符显示,也必须转义。
自定义 token
definitions 会与内置 token 合并;相同 key 会覆盖内置定义。
interface MaskTokenDefinition { readonly pattern: RegExp readonly required?: boolean readonly transform?: (character: string) => string} type MaskTokenDefinitions = Readonly<Record<string, MaskTokenDefinition>>
示例:
const upperHexDefinitions: MaskTokenDefinitions = { X: { pattern: /[0-9a-f]/i, required: true, transform: character => character.toUpperCase(), },} const colorCode = new RenderMaskedTextEdit({ mask: '\\#XXXXXX', definitions: upperHexDefinitions, value: '12abef',}) colorCode.value // '12ABEF'colorCode.displayValue // '#12ABEF'
自定义定义的约束:
pattern应检查单个 Unicode code point。required省略时按true处理。transform必须返回一个 Unicode code point。- 转换后的字符仍必须通过
pattern,否则会被丢弃。 - 可选 token 是顺序槽位,不能跳过中间可选槽位后继续填写后面的必填槽位。因此可选 token 优先放在 mask 尾部。
构造参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mask |
string |
必填 | 固定格式。必须非空,并至少包含一个内置或自定义 token。 |
value |
string |
'' |
初始 raw value。 |
definitions |
MaskTokenDefinitions |
内置定义 | 新增或覆盖 token 定义。 |
placeholder |
string |
'' |
空值提示。不是 mask prompt character。 |
readonly |
boolean |
false |
可聚焦和选择,但不能修改、粘贴或清除。 |
disabled |
boolean |
false |
不可聚焦和编辑。 |
status |
FormFieldStatus |
'default' |
字段状态色。 |
helperText |
string |
'' |
字段下方的帮助或错误文字。 |
prefixText |
string |
'' |
mask 显示区域前的装饰文本,不属于 raw/display value。 |
suffixText |
string |
'' |
mask 显示区域后的装饰文本,不属于 raw/display value。 |
clearable |
boolean |
false |
非空、非只读、非禁用时显示清除按钮。 |
fieldHeight |
number |
主题默认高度 | 输入主体高度,不包含 helperText。 |
onChange |
(value: string, details: MaskValue) => void |
undefined |
用户输入、粘贴、删除或清除完成一次规范化后触发。 |
onSubmit |
(value: string, details: MaskValue) => void |
undefined |
按 Enter 时触发。 |
onBlur |
(value: string, details: MaskValue) => void |
undefined |
输入框失去焦点时触发。 |
构造 MaskEngine 时,空 mask 或不包含任何 token 的 mask 会直接抛出错误,避免生成一个看似可编辑、实际上没有输入槽位的字段。
MaskValue
interface MaskValue { readonly raw: string readonly display: string readonly complete: boolean readonly rawToDisplay: readonly number[] readonly displayToRaw: readonly number[]}
| 字段 | 说明 |
|---|---|
raw |
已接受的 raw value。 |
display |
当前格式化文本。 |
complete |
最后一个必填槽位是否已经填写。可选尾部槽位为空时仍可为 true。 |
rawToDisplay |
raw 光标边界到 display 光标边界的映射,索引和值都使用 JavaScript 的 UTF-16 code unit 偏移。长度为 raw.length + 1。 |
displayToRaw |
display 光标边界到 raw 光标边界的映射,索引和值都使用 JavaScript 的 UTF-16 code unit 偏移。长度为 display.length + 1。 |
普通表单通常只使用 raw、display 和 complete。两个位置映射主要供自定义编辑宿主、诊断和精确恢复选区使用。编辑器按完整 code point 占用一个 mask 槽位;如果浏览器给出的选区落在代理对内部,替换和删除会安全地归到该槽位边界。
这里承诺的是 Unicode code point,而不是用户感知的 grapheme cluster。由多个 code point 组成的 emoji、变音组合或复杂文字簇会占用多个槽位;需要按字素簇编辑时,应使用专门的文本编辑组件。
属性和方法
| API | 类型 | 说明 |
|---|---|---|
value |
string |
当前 raw value。可读写;写入时重新规范化,但不触发回调。 |
displayValue |
string |
当前格式化显示值,只读。 |
complete |
boolean |
当前是否已填写全部必填槽位,只读。 |
engine |
MaskEngine |
当前组件使用的 mask 引擎,只读。mask 在组件构造后不可替换。 |
textBox |
RenderTextBox |
内部文本输入对象,供诊断和高级宿主使用。普通业务不应直接改它的 value。 |
onChange |
回调或 undefined |
可在运行时替换。 |
onSubmit |
回调或 undefined |
可在运行时替换。 |
onBlur |
回调或 undefined |
可在运行时替换。 |
placeholder |
string |
提示文字。 |
readonly |
boolean |
只读状态。 |
disabled |
boolean |
禁用状态。 |
status |
FormFieldStatus |
字段状态。 |
helperText |
string |
帮助或错误文字。 |
clearable |
boolean |
是否允许清除。 |
isFocused |
boolean |
当前是否拥有输入焦点,只读。 |
reset(value = '') |
void |
使用新的 raw value 重置字段和选区,不触发回调。 |
focusIn() |
void |
进入输入焦点。通常由焦点系统调用。 |
focusOut() |
void |
结束输入并触发 onBlur。通常由焦点系统调用。 |
不要直接写 edit.textBox.value。这会绕过 raw/display 同步;业务应写 edit.value 或调用 reset()。
MaskEngine
MaskEngine 是无界面的纯值工具,可用于 DataGrid 编辑适配、批量格式化和单元测试:
const engine = new MaskEngine('000-000') engine.formatRaw('1234').display// '123-4' engine.normalize('123-456').raw// '123456'
| API | 说明 |
|---|---|
mask |
原始 mask 字符串。 |
definitions |
合并后的 token 定义。 |
capacity |
可编辑槽位总数。 |
formatRaw(raw) |
按 raw value 生成 MaskValue。 |
normalize(value) |
规范化可能包含 mask 固定字符的显示文本,生成 MaskValue。纯 raw value 应使用 formatRaw()。 |
normalizeDisplay(display, start?, end?) |
规范化显示文本,并返回修正后的选区。 |
replace(display, start, end, text) |
按 mask 槽位替换显示选区。 |
delete(display, start, end, direction) |
删除选区、前一个槽位或后一个槽位。 |
normalizeDisplay、replace 和 delete 返回 MaskEditResult,它在 MaskValue 基础上增加 selectionStart 和 selectionEnd。
键盘、粘贴和 IME
| 操作 | 行为 |
|---|---|
| 普通输入 | 只保留符合当前槽位的字符,并自动插入固定字符。 |
| Backspace | 删除选区;没有选区时删除前一个值槽位,不会卡在分隔符上。 |
| Delete | 删除选区;没有选区时删除后一个值槽位。 |
| 粘贴 | 替换当前选区,过滤无效字符,重新计算 display value 和光标。 |
| Enter | 触发 onSubmit,随后按基础 TextBox 行为退出焦点。 |
| Escape | 退出焦点。 |
| 方向键、全选、复制、剪切 | 复用 TextBox 的选区和共享输入器行为。 |
Ctrl/Cmd + Z |
撤销最近一次已生效的 mask 编辑;最多保留 100 个历史快照。 |
Ctrl/Cmd + Shift + Z、Ctrl + Y |
重做最近一次撤销。 |
| IME composition update | 只显示输入法组合过程,不改 raw value。 |
| IME composition end | 按当前 token 规则接收或丢弃最终字符;整个 composition 作为一次历史事务。只有 raw/display 实际变化时才触发 onChange。 |
L、?、A、a 使用 Unicode 字母规则,所以可以接受中文。数字 token 会在 IME 确认后拒绝中文输入。
回调时机
onChange的第一参数始终是 raw value。details是同一时刻的完整MaskValue。- 固定字符插入、无效字符过滤和选区修正都在回调之前完成。
- 只有规范化后的值实际变化时才触发
onChange;被 mask 完全拒绝的输入不会产生不变值通知。 - 程序写入
value和调用reset()不触发onChange,同时会清空当前撤销/重做历史,避免撤销回到已经失效的外部状态。 onSubmit和onBlur会先根据当前显示文本刷新 raw/display,再执行回调。
只读和禁用
| 状态 | 可聚焦 | 可选择/复制 | 可编辑 | 可清除 |
|---|---|---|---|---|
| 正常 | 是 | 是 | 是 | 取决于 clearable |
readonly |
是 | 是 | 否 | 否 |
disabled |
否 | 否 | 否 | 否 |
当前边界
- mask 是构造期配置,组件不提供运行时替换 mask 的 setter。mask 变化时应重建组件。
- 不显示未填写槽位的下划线或 prompt character。
- 不内置正负数、小数、千分位、日期合法性或时间范围校验;这些应由对应的专业输入组件处理。
- 可选槽位采用顺序填充,推荐只把可选 token 放在末尾。
complete只表示必填槽位已填满,不代表业务值一定有效。例如2026-99-99可以填满日期形状,但仍需业务日期校验。- 当前没有作为 DataGrid 内置列编辑类型;需要表格内使用时,应由后续明确的 Grid editor contract 接入。