在合同、表单与公文模板这类文档中,往往只希望填写人修改其中少数几处内容(例如签署信息、项目名称、验收结论),其余条款必须保持原样。为文档设置可编辑区域,就能把"允许改哪里"精确限定下来,其余内容一律只读。Spire.Doc for JavaScript 基于 WebAssembly 在浏览器端直接完成这一处理,通过虚拟文件系统(VFS)管理字体与文件资源,无需后端服务支持。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。以下示例默认已安装 Spire.Doc 并完成 WebAssembly 模块初始化。
设置可编辑区域
设置可编辑区域的核心流程分为三个阶段:首先通过 FetchFileToVFS 将字体文件和目标 Word 文档载入 WASM 虚拟文件系统;然后实例化 Document 加载文件,调用 Protect 将整篇文档设为只读,再创建一对 id 相同的 PermissionStart 与 PermissionEnd 标记,把指定段落标记为可编辑区域;最后保存文档并从 VFS 读取生成的文件,封装为 Blob 后触发浏览器下载。
function App() {
const SetEditableRange = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将输入文档载入 VFS
const inputFileName = "SetEditableRange.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// 创建文档对象并加载文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 保护整篇文档:除可编辑区域外,其余内容一律只读
doc.Protect({ type: docModule.ProtectionType.AllowOnlyReading, password: "password" });
// 创建权限标记,id 相同的 start 与 end 构成一个可编辑区域
const start = new docModule.PermissionStart(doc, "testID");
const end = new docModule.PermissionEnd(doc, "testID");
// 将标记插入第一个段落:起点置于段首,终点追加到段尾
doc.Sections.get_Item(0).Paragraphs.get_Item(0).ChildObjects.Insert(0, start);
doc.Sections.get_Item(0).Paragraphs.get_Item(0).ChildObjects.Add(end);
// 保存文档
const outputFileName = "设置编辑区域.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
doc.Dispose();
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { 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>设置Word文档内容的编辑区域</h1>
<button onClick={SetEditableRange}>
开始
</button>
</div>
);
}
export default App;
示例文档中用浅色底纹标出需要填写的位置(仅为视觉提示,与可编辑区域的设置无关)。设置可编辑区域后,只有带底纹的段落可以修改,其余条款在 Word 中处于只读状态。

删除可编辑区域
删除可编辑区域只需一次遍历:依次访问文档的每个节、每个段落,在段落的 ChildObjects 集合中查找 PermissionStart 与 PermissionEnd 对象,命中即从集合中移除。
这里有一个容易踩坑的细节:ChildObjects.Remove 执行后集合会立即缩短,后续元素的下标整体前移。因此移除时不能再递增下标,否则每删掉一个标记就会跳过紧随其后的一个,造成漏删。
function App() {
const RemoveEditableRange = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将输入文档载入 VFS
const inputFileName = "RemoveEditableRange.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// 创建文档对象并加载文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 遍历所有节与段落,删除权限标记
for (let i = 0; i < doc.Sections.Count; i++) {
const section = doc.Sections.get_Item(i);
for (let j = 0; j < section.Body.Paragraphs.Count; j++) {
const paragraph = section.Body.Paragraphs.get_Item(j);
// 命中即移除,移除后集合缩短、下标不递增
for (let k = 0; k < paragraph.ChildObjects.Count;) {
const obj = paragraph.ChildObjects.get_Item(k);
if (obj instanceof docModule.PermissionStart || obj instanceof docModule.PermissionEnd) {
paragraph.ChildObjects.Remove(obj);
} else {
k++;
}
}
}
}
// 保存文档
const outputFileName = "移除编辑区域.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 释放资源
doc.Dispose();
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { 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>移除Word文档中的编辑区域</h1>
<button onClick={RemoveEditableRange}>
开始
</button>
</div>
);
}
export default App;
删除标记只影响可编辑区域的划分,文档的文字内容与格式不会发生任何变化。

常见问题
设置了可编辑区域,但可编辑范围内的内容仍然无法编辑
原因:权限标记必须与文档的编辑限制配合才会生效。只插入 PermissionStart 与 PermissionEnd 而不调用 Protect,文档并未进入保护状态,标记不会产生任何效果;此外两个标记的 id 必须完全一致,Word 才会把它们识别为同一个可编辑区域。
解决:先启用编辑限制,再用同一个 id 创建成对的标记:
// 先启用保护,标记才会有意义
document.Protect({ type: wasmModule.ProtectionType.AllowOnlyReading, password: "password" });
// start 与 end 必须使用相同的 id
const start = new wasmModule.PermissionStart(document, "testID");
const end = new wasmModule.PermissionEnd(document, "testID");
删除可编辑区域时出现漏删
原因:ChildObjects.Remove 会让集合中后续元素的下标整体前移。如果在 for 循环中一边递增下标一边删除,每删除一个对象就会跳过紧随其后的一个,文档中残留的标记数量越多,漏删越明显。
解决:改为"命中即删除、下标不递增",或者先收集待删除对象再倒序遍历:
for (let k = 0; k < paragraph.ChildObjects.Count;) {
const obj = paragraph.ChildObjects.get_Item(k);
if (obj instanceof wasmModule.PermissionStart || obj instanceof wasmModule.PermissionEnd) {
paragraph.ChildObjects.Remove(obj);
// 此处不递增 k,继续检查当前下标上的新对象
} else {
k++;
}
}
删除标记后文档仍然处于只读状态
原因:PermissionStart 与 PermissionEnd 只是"允许编辑哪些区域"的标记,删除它们并不会关闭文档的编辑限制。文档的保护依然生效,此时整篇文档都不可编辑。
解决:如果确认不再需要保护,可在删除标记之后再调用 Unprotect;若文档设置了密码,请传入当初设置保护时的密码:
document.Unprotect("password");
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







