一份由模板生成、又经过多人填写的文档,里面往往散布着几十个内容控件。要核对哪些字段被改过、哪些控件还保留着模板标记,逐个点开属性面板效率很低。如果能把所有控件的别名、ID、标记和类型导出成一张表,审阅工作就会轻很多。同样的遍历方式也可以反过来用:按类型批量修改控件的颜色和外观,让不同用途的字段在文档中一眼可辨。
本文用 Spire.Doc for JavaScript 完成三件事:读取控件属性并导出清单、按类型修改控件颜色、按类型设置显示外观。文档在浏览器中通过虚拟文件系统加载,处理结果直接下载,不经过服务器。
本文介绍三个功能点:
读取与修改都依赖同一套遍历逻辑,先明确两类控件的对象类型:
| 对象类型 | DocumentObjectType 取值 |
所在位置 |
|---|---|---|
| 行内内容控件 | StructureDocumentTagInline |
段落的 ChildObjects 中 |
| 块级内容控件 | StructureDocumentTag |
节正文或单元格的 ChildObjects 中 |
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。下面的示例假定 Spire.Doc 已安装、WebAssembly 模块已初始化。
读取内容控件的属性
读取的目标是把文档中每个内容控件的关键属性汇总成文本。示例从 public/data 读取 ContentControl.docx,运行时请把该文件放入项目的 public/data 目录。需要处理三种存放位置:正文段落中的行内控件、表格单元格中的行内控件,以及正文中的块级控件。三个位置都遍历到,清单才不会遗漏。
读取的属性包括:
| 属性 | 含义 |
|---|---|
SDTProperties.Alias |
控件别名,Word 中显示的名称 |
SDTProperties.Id |
控件 ID,文档内唯一 |
SDTProperties.Tag |
控件标记,通常由模板预先写入 |
SDTProperties.SDTType |
控件类型,如 Text、DropDownList |
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 载入字体,把待读取的
ContentControl.docx读入 VFS 并加载为Document。 - 遍历正文段落与表格单元格,收集行内控件;再收集正文中的块级控件。
- 依次读取每个控件的别名、ID、标记与类型,拼成制表符分隔的文本。
- 把文本写入 VFS 并触发下载,得到可导入表格软件的清单文件。
读取内容控件属性的代码如下:
function App() {
const readContentControlProperties = async () => {
// 获取 Spire.Doc 的 WASM 模块
const wasmModule = window.wasmModule?.spiredoc;
if (!wasmModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入字体与待读取的文档
await window.spire.FetchFileToVFS('ContentControl.docx', '', `${process.env.PUBLIC_URL}/data/`);
const doc = new wasmModule.Document();
doc.LoadFromFile('ContentControl.docx');
// 收集行内控件与块级控件
const found = [];
const blocks = [];
const scanParagraph = (paragraph) => {
for (let j = 0; j < paragraph.ChildObjects.Count; j++) {
const child = paragraph.ChildObjects.get_Item(j);
if (child.DocumentObjectType === wasmModule.DocumentObjectType.StructureDocumentTagInline) {
found.push(child);
}
}
};
const scanTable = (table) => {
for (let r = 0; r < table.Rows.Count; r++) {
const row = table.Rows.get_Item(r);
for (let c = 0; c < row.Cells.Count; c++) {
const cell = row.Cells.get_Item(c);
for (let k = 0; k < cell.ChildObjects.Count; k++) {
const child = cell.ChildObjects.get_Item(k);
if (child.DocumentObjectType === wasmModule.DocumentObjectType.Paragraph) scanParagraph(child);
if (child.DocumentObjectType === wasmModule.DocumentObjectType.StructureDocumentTagInline) found.push(child);
}
}
}
};
for (let s = 0; s < doc.Sections.Count; s++) {
const section = doc.Sections.get_Item(s);
for (let i = 0; i < section.Body.ChildObjects.Count; i++) {
const obj = section.Body.ChildObjects.get_Item(i);
if (obj.DocumentObjectType === wasmModule.DocumentObjectType.Paragraph) scanParagraph(obj);
if (obj.DocumentObjectType === wasmModule.DocumentObjectType.Table) scanTable(obj);
if (obj.DocumentObjectType === wasmModule.DocumentObjectType.StructureDocumentTag) blocks.push(obj);
}
}
// 拼接属性清单
let report = '别名\tID\t标记\t类型\r\n';
for (const sdt of [...found, ...blocks]) {
const properties = sdt.SDTProperties;
const type = String(properties.SDTType).replace('SdtType.', '');
report += `${properties.Alias}\t${properties.Id}\t${properties.Tag}\t${type}\r\n`;
}
// 写入 VFS 并下载文本文件
window.dotnetRuntime.Module.FS.writeFile('GetContentControlProperty.txt', report);
doc.Dispose();
const fileArray = window.dotnetRuntime.Module.FS.readFile('GetContentControlProperty.txt');
const blob = new Blob([fileArray], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = '获取内容控件属性.txt';
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>读取内容控件属性</h1>
<button onClick={readContentControlProperties}>导出属性清单</button>
</div>
);
}
export default App;
示例文档的正文与表格如下:

导出的清单内容如下:
| 别名 | ID | 标记 | 类型 |
|---|---|---|---|
| 姓名 | 2001 | employee-name | Text |
| 所属部门 | 2002 | department | DropDownList |
| 入职日期 | 2003 | hire-date | DatePicker |
| 笔记本电脑 | 2004 | laptop | CheckBox |
| 外接显示器 | 2005 | monitor | CheckBox |
| 入职须知 | 2006 | onboarding-notice | RichText |
遍历时如何兼顾表格与块级控件
正文直属段落只能覆盖一部分控件。表格中的控件要先进入 Table.Rows、Row.Cells,再扫描单元格的 ChildObjects;块级控件则直接出现在 Body.ChildObjects 中,其内容不在段落里。示例把三种情况分开处理,再把结果合并输出。
遍历过程中应先判断 DocumentObjectType 再访问属性。对普通段落读取 SDTProperties 会返回空值或触发异常,先过滤类型可以避免这类问题。
按类型修改内容控件的颜色
当文档中同时存在文本、富文本、日期等多种控件时,用颜色区分用途比逐个看别名更直观。示例从 public/data 读取 ModifySTDColor.docx,运行时请把该文件放入项目的 public/data 目录。例如把所有文本控件标为绿色,把所有富文本控件标为橙色。颜色写在 SDTProperties.Color 上,按控件类型分别赋值即可。
修改颜色涉及以下成员:
| 成员 | 作用 |
|---|---|
SDTProperties.SDTType |
判断控件类型,决定赋予哪种颜色 |
SDTProperties.Color |
控件颜色,需使用 Color.FromArgb() 构造 |
Color.FromArgb(a, r, g, b) |
以 ARGB 分量创建颜色 |
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 载入字体,把待处理的
ModifySTDColor.docx读入 VFS 并加载为Document。 - 遍历正文中的段落与块级控件,取出每个内容控件对象。
- 按
SDTType分支,为文本控件赋绿色、为富文本控件赋橙色。 - 使用
SaveToFile()保存结果文件,从 VFS 读出并触发浏览器下载。
按类型修改内容控件颜色的代码如下:
function App() {
const modifySDTColor = async () => {
// 获取 Spire.Doc 的 WASM 模块
const wasmModule = window.wasmModule?.spiredoc;
if (!wasmModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入字体与待处理的文档
await window.spire.FetchFileToVFS('ModifySTDColor.docx', '', `${process.env.PUBLIC_URL}/data/`);
const doc = new wasmModule.Document();
doc.LoadFromFile('ModifySTDColor.docx');
// 遍历各节,按类型修改控件颜色
for (let s = 0; s < doc.Sections.Count; s++) {
const section = doc.Sections.get_Item(s);
for (let i = 0; i < section.Body.ChildObjects.Count; i++) {
const obj = section.Body.ChildObjects.get_Item(i);
const applyColor = (sdt) => {
switch (sdt.SDTProperties.SDTType) {
case wasmModule.SdtType.RichText:
sdt.SDTProperties.Color = wasmModule.Color.FromArgb(255, 237, 125, 49);
break;
case wasmModule.SdtType.Text:
sdt.SDTProperties.Color = wasmModule.Color.FromArgb(255, 0, 128, 0);
break;
}
};
if (obj.DocumentObjectType === wasmModule.DocumentObjectType.StructureDocumentTag) {
applyColor(obj);
}
if (obj.DocumentObjectType === wasmModule.DocumentObjectType.Paragraph) {
for (let j = 0; j < obj.ChildObjects.Count; j++) {
const child = obj.ChildObjects.get_Item(j);
if (child.DocumentObjectType === wasmModule.DocumentObjectType.StructureDocumentTagInline) {
applyColor(child);
}
}
}
}
}
// 保存结果文件
const outputFileName = 'ModifySDTColor_out.docx';
doc.SaveToFile({ fileName: outputFileName, fileFormat: wasmModule.FileFormat.Docx2013 });
doc.Dispose();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>按类型修改内容控件颜色</h1>
<button onClick={modifySDTColor}>生成文档</button>
</div>
);
}
export default App;
修改颜色后的文档如下,文本控件的标签为绿色,富文本控件的标签为橙色:

控件颜色与文字颜色不是一回事
SDTProperties.Color 设置的是控件标签的颜色,不是控件内文字的颜色。文字颜色属于 TextRange.CharacterFormat.TextColor,需要逐个文本片段设置。两者各管一段:前者标识控件本身,后者控制正文排版。
控件颜色只在 Appearance 为 Tags 时可见。若文档使用默认外观,赋了颜色也看不出变化,需要同时设置 SdtAppearance.Tags,或者改用后文的边框外观。
按类型设置内容控件的显示外观
外观决定内容控件在 Word 编辑视图中的呈现方式,示例同样从 public/data 读取 ContentControl.docx。填写阶段希望字段边界清晰,可以把文本、下拉列表、日期控件设为标签外观;审阅阶段希望版面干净,可以把复选框设为隐藏外观,只保留方框符号。按类型分别设置,就能在同一份文档中兼顾不同用途。
外观由 SDTProperties.Appearance 控制,常用取值如下:
| 取值 | 显示效果 | 适用场景 |
|---|---|---|
SdtAppearance.Tags |
在内容两侧显示别名标签 | 需要明确字段名称的填写项 |
SdtAppearance.BoundingBox |
以边框圈出控件范围 | 块级内容、整段说明 |
SdtAppearance.Hidden |
不显示控件外框 | 复选框等本身已有视觉标记的控件 |
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 载入字体,把待处理的
ContentControl.docx读入 VFS 并加载为Document。 - 遍历正文中的段落与块级控件,取出每个内容控件对象。
- 按
SDTType分支设置Appearance:文本、下拉列表、日期用标签,富文本用边框,复选框用隐藏。 - 使用
SaveToFile()保存结果文件,从 VFS 读出并触发浏览器下载。
按类型设置内容控件外观的代码如下:
function App() {
const setContentControlAppearance = async () => {
// 获取 Spire.Doc 的 WASM 模块
const wasmModule = window.wasmModule?.spiredoc;
if (!wasmModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入待处理的文档
await window.spire.FetchFileToVFS('ContentControl.docx', '', `${process.env.PUBLIC_URL}/data/`);
const doc = new wasmModule.Document();
doc.LoadFromFile('ContentControl.docx');
// 遍历各节,按类型设置显示外观
for (let s = 0; s < doc.Sections.Count; s++) {
const section = doc.Sections.get_Item(s);
for (let i = 0; i < section.Body.ChildObjects.Count; i++) {
const obj = section.Body.ChildObjects.get_Item(i);
const applyAppearance = (sdt) => {
switch (sdt.SDTProperties.SDTType) {
case wasmModule.SdtType.Text:
case wasmModule.SdtType.DropDownList:
case wasmModule.SdtType.DatePicker:
sdt.SDTProperties.Appearance = wasmModule.SdtAppearance.Tags;
break;
case wasmModule.SdtType.RichText:
sdt.SDTProperties.Appearance = wasmModule.SdtAppearance.BoundingBox;
break;
case wasmModule.SdtType.CheckBox:
sdt.SDTProperties.Appearance = wasmModule.SdtAppearance.Hidden;
break;
}
};
if (obj.DocumentObjectType === wasmModule.DocumentObjectType.StructureDocumentTag) {
applyAppearance(obj);
}
if (obj.DocumentObjectType === wasmModule.DocumentObjectType.Paragraph) {
for (let j = 0; j < obj.ChildObjects.Count; j++) {
const child = obj.ChildObjects.get_Item(j);
if (child.DocumentObjectType === wasmModule.DocumentObjectType.StructureDocumentTagInline) {
applyAppearance(child);
}
}
}
}
}
// 保存结果文件
const outputFileName = 'SetContentControlAppearance_out.docx';
doc.SaveToFile({ fileName: outputFileName, fileFormat: wasmModule.FileFormat.Docx2013 });
doc.Dispose();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>按类型设置内容控件外观</h1>
<button onClick={setContentControlAppearance}>生成文档</button>
</div>
);
}
export default App;
设置外观后的文档如下:

外观只影响编辑视图
Appearance 是给编辑者看的视觉提示,不会改变控件类型、内容或锁定状态。把外观设为 Hidden 后,控件在 Word 中不再显示外框,但依然可以选中、可以填写;打印和导出 PDF 时也不会留下标签或边框。需要限制填写行为时,应使用锁定设置,而不是依赖外观。
常见问题
为什么设置了控件颜色却看不到变化
SDTProperties.Color 作用于控件标签,只有在 Appearance 为 Tags 时才显示。若文档仍使用默认外观,颜色不会出现在页面上。把 Appearance 设为 SdtAppearance.Tags 后再保存,标签就会带上设定的颜色。
如何判断一个控件是行内还是块级
读取 DocumentObjectType 即可:StructureDocumentTagInline 是行内控件,位于段落内部;StructureDocumentTag 是块级控件,直接位于节正文或单元格中。两者都通过 SDTProperties 访问属性,但遍历入口不同,块级控件不需要经过段落。
表格中的内容控件为什么没有读到
表格内容不在正文段落的 ChildObjects 里,需要先进入 Table.Rows,再访问 Row.Cells,最后扫描 Cell.ChildObjects。示例中的 scanTable 专门处理这一层,如果只遍历正文段落,表格里的字段就会被漏掉。
新建的控件读回来类型为什么不是设定的值
类型以保存后的文档内容为准。创建控件时应显式设置 SDTType,再写入内容并保存;若省略类型或依赖默认值,部分控件在回读时会被识别为 RichText。读取已有文档时,SDTType 反映的是文档中实际保存的类型,可以据此核对模板是否按预期生成。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







