教程:创建 Word 任务窗格加载项
在本教程中,将创建 Word 任务窗格加载项,该加载项将:
- 插入文本区域
- 设置文本格式
- 替换文本并在各个位置插入文本
- 插入图像、HTML 和表格
- 创建和更新内容控件
提示
如果已完成了创建首个 Word 任务窗格加载项快速入门,并希望使用该项目作为本教程的起点,请直接转到插入文本区域以开始此教程。
如果需要本教程的完整版本,请转到 GitHub 上的 Office 外接程序示例存储库。
先决条件
Node.js(最新LTS 版本)。 访问 Node.js 站点 ,下载并安装适合你的操作系统的版本。
最新版本的 Yeoman 和适用于 Office 加载项的 Yeoman 生成器。若要全局安装这些工具,请从命令提示符处运行以下命令。
npm install -g yo generator-office
注意
即便先前已安装了 Yeoman 生成器,我们还是建议你通过 npm 将包更新为最新版本。
已连接到 Microsoft 365 订阅的 Office (包括 Office 网页版)。
注意
如果还没有 Office,可以通过 Microsoft 365 开发人员计划获得Microsoft 365 E5开发人员订阅;有关详细信息,请参阅常见问题解答。 或者,可以 注册 1 个月的免费试用版 或 购买 Microsoft 365 计划。
创建加载项项目
运行以下命令,使用 Yeoman 生成器创建加载项项目。 包含项目的文件夹将添加到当前目录。
yo office
注意
运行该yo office
命令时,可能会收到有关 Yeoman 和 Office 加载项 CLI 工具的数据收集策略的提示。 根据你的需要,使用提供的信息来响应提示。
出现提示时,请提供以下信息以创建加载项项目。
-
选择项目类型:
Office Add-in Task Pane project
-
选择脚本类型:
JavaScript
-
要为外接程序命名什么名称?
My Office Add-in
-
你希望支持哪个 Office 客户端应用程序?
Word
完成此向导后,生成器会创建项目,并安装支持的 Node 组件。
插入文本区域
本教程的这一步是,先以编程方式测试加载项是否支持用户的当前版本 Word,再在文档中插入段落。
编码加载项
在代码编辑器中打开项目。
打开 ./src/taskpane/taskpane.html 文件。 此文件含有任务窗格的 HTML 标记。
找到
<main>
元素并删除在开始<main>
标记后和关闭</main>
标记前出现的所有行。打开
<main>
标记后立即添加下列标记:<button class="ms-Button" id="insert-paragraph">Insert Paragraph</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。 此文件包含用于加快任务窗格与 Office 客户端应用程序之间的交互的 Office JavaScript API 代码。
执行以下操作,删除对
run
按钮和run()
函数的所有引用:查找并删除行
document.getElementById("run").onclick = run;
。查找并删除整个
run()
函数。
在
Office.onReady
函数调用中,找到行if (info.host === Office.HostType.Word) {
并紧跟该行添加下列代码。 注意:- 此代码为
insert-paragraph
按钮添加事件处理程序。 - 函数
insertParagraph
包装在调用tryCatch
中, (将在下一步) 中添加这两个函数。 这允许与服务代码分开处理 Office JavaScript API 层生成的任何错误。
// Assign event handlers and other initialization logic. document.getElementById("insert-paragraph").onclick = () => tryCatch(insertParagraph);
- 此代码为
将以下函数添加到文件末尾。 注意:
Word.js 业务逻辑将添加到传递给 的
Word.run
函数中。 此逻辑不立即执行。 相反,它会添加到挂起的命令队列中。context.sync
方法将所有已排入队列的命令都发送到 Word 以供执行。通过
tryCatch
任务窗格与工作簿交互的所有函数都将使用该函数。 以这种方式捕获 Office JavaScript 错误是一种通用处理未捕获的错误的便捷方法。
async function insertParagraph() { await Word.run(async (context) => { // TODO1: Queue commands to insert a paragraph into the document. await context.sync(); }); } /** Default helper for invoking an action and handling errors. */ async function tryCatch(callback) { try { await callback(); } catch (error) { // Note: In a production add-in, you'd want to notify the user through your add-in's UI. console.error(error); } }
在
insertParagraph()
函数中,将TODO1
替换为以下代码。 注意:insertParagraph
方法的第一个参数是新段落的文本。第二个参数是应在正文中的什么位置插入段落。 如果父对象为正文,其他段落插入选项包括“End”和“Replace”。
const docBody = context.document.body; docBody.insertParagraph("Office has several versions, including Office 2016, Microsoft 365 subscription, and Office on the web.", Word.InsertLocation.start);
保存对项目所做的所有更改。
测试加载项
完成以下步骤,以启动本地 Web 服务器并旁加载你的加载项。
注意
即使在开发过程中,Office 外接程序也应使用 HTTPS,而不是 HTTP。 如果在运行以下命令之一后系统提示安装证书,请接受安装 Yeoman 生成器提供的证书的提示。 你可能还必须以管理员身份运行命令提示符或终端才能进行更改。
如果这是你第一次在计算机上开发 Office 加载项,则命令行中可能会提示你授予Microsoft Edge WebView 环回豁免 (“允许 Microsoft Edge WebView 的 localhost 环回?”) 。 出现提示时,输入
Y
以允许豁免。 请注意,需要管理员权限才能允许豁免。 一旦允许,在将来 (旁加载 Office 加载项时,系统就不会提示你获得豁免,除非从计算机) 中删除该豁免。 若要了解详细信息,请参阅加载 Office 外接程序或使用 Fiddler 时,“我们无法从 localhost 打开此外接程序”。
提示
如果在 Mac 上测试加载项,请先运行项目根目录中的以下命令,然后再继续。 运行此命令时,本地 Web 服务器将启动。
npm run dev-server
若要在 Word 中测试加载项,请在项目的根目录中运行以下命令。 如果本地 Web 服务器尚未运行) ,则会启动本地 Web 服务器 (,并在加载加载项时打开Word。
npm start
若要在 Word 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将“{url}”替换为你有权访问的 OneDrive 或 SharePoint 库中 Word 文档的 URL。
注意
如果在 Mac 上进行开发,请将 括
{url}
在单引号中。 请勿在 Windows 上执行此操作。npm run start:web -- --document {url}
示例如下。
npm run start:web -- --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版。
在 Word中,如果“我的 Office 外接程序”任务窗格尚未打开,请选择“开始”选项卡,然后选择功能区上的“显示任务窗格”按钮以打开外接程序任务窗格。
在任务窗格中,选择“插入段落”按钮。
在段落中进行一些更改。
再次选择“插入段落”按钮。 观察新段落是否位于上一段落之上,因为
insertParagraph
方法要在文档正文的“开头”插入内容。如果要停止本地 Web 服务器并卸载加载项,请按照适用的说明操作:
若要停止服务器,请运行以下命令。 如果使用
npm start
,则以下命令也会卸载加载项。npm stop
如果手动旁加载加载项,请参阅 删除旁加载加载项。
设置文本格式
在本教程的此步骤中,你将向文本应用嵌入样式、向文本应用自定义样式并更改文本字体。
向文本应用嵌入样式
打开 ./src/taskpane/taskpane.html 文件。
查找
insert-paragraph
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="apply-style">Apply Style</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到insert-paragraph
按钮的行,并在该行后添加以下代码。document.getElementById("apply-style").onclick = () => tryCatch(applyStyle);
将以下函数添加到文件结尾。
async function applyStyle() { await Word.run(async (context) => { // TODO1: Queue commands to style text. await context.sync(); }); }
在
applyStyle()
函数中,将TODO1
替换为以下代码。 请注意,此代码向段落应用样式,但也可以向文本区域应用样式。const firstParagraph = context.document.body.paragraphs.getFirst(); firstParagraph.styleBuiltIn = Word.Style.intenseReference;
向文本应用自定义样式
打开 ./src/taskpane/taskpane.html 文件。
查找
apply-style
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="apply-custom-style">Apply Custom Style</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到apply-style
按钮的行,并在该行后添加以下代码。document.getElementById("apply-custom-style").onclick = () => tryCatch(applyCustomStyle);
将以下函数添加到文件结尾。
async function applyCustomStyle() { await Word.run(async (context) => { // TODO1: Queue commands to apply the custom style. await context.sync(); }); }
在
applyCustomStyle()
函数中,将TODO1
替换为以下代码。 请注意,此代码应用的自定义样式尚不存在。 将在测试加载项步骤中创建 MyCustomStyle 样式。const lastParagraph = context.document.body.paragraphs.getLast(); lastParagraph.style = "MyCustomStyle";
保存对项目所做的所有更改。
更改文本字体
打开 ./src/taskpane/taskpane.html 文件。
查找
apply-custom-style
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="change-font">Change Font</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到apply-custom-style
按钮的行,并在该行后添加以下代码。document.getElementById("change-font").onclick = () => tryCatch(changeFont);
将以下函数添加到文件结尾。
async function changeFont() { await Word.run(async (context) => { // TODO1: Queue commands to apply a different font. await context.sync(); }); }
在
changeFont()
函数中,将TODO1
替换为以下代码。 请注意,此代码使用链接到Paragraph.getNext
方法的ParagraphCollection.getFirst
方法,获取对第二个段落的引用。const secondParagraph = context.document.body.paragraphs.getFirst().getNext(); secondParagraph.font.set({ name: "Courier New", bold: true, size: 18 });
保存对项目所做的所有更改。
测试加载项
如果本地 Web 服务器已在运行,并且加载项已加载到 Word 中,则继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项。
若要在 Word 中测试加载项,请在项目的根目录中运行以下命令。 如果本地 Web 服务器尚未运行) ,则会启动本地 Web 服务器 (,并在加载加载项时打开Word。
npm start
若要在 Word 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将“{url}”替换为你有权访问的 OneDrive 或 SharePoint 库中 Word 文档的 URL。
注意
如果在 Mac 上进行开发,请将 括
{url}
在单引号中。 请勿在 Windows 上执行此操作。npm run start:web -- --document {url}
示例如下。
npm run start:web -- --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版。
如果加载项任务窗格尚未在Word中打开,请转到“开始”选项卡,然后选择功能区上的“显示任务窗格”按钮将其打开。
请确保文档中至少有三个段落。 可以选择“插入段落”按钮三次。 仔细检查文档末尾是否没有空白段落。 如果有,请将其删除。
在 Word 中,创建自定义样式“MyCustomStyle”。 其中可以包含所需的任何格式。
选择“应用样式”按钮。 第一个段落将采用嵌入样式“明显参考”。
选择“应用自定义样式”按钮。 最后一个段落将采用自定义样式。 (如果似乎未发生任何操作,则最后一段可能为空。如果是这样,请向其添加一些文本。)
选择“更改字体”按钮。 第二个段落的字体更改为 18 磅的粗体 Courier New。
替换文本和插入文本
本教程的这一步是,在选定文本区域内外添加文本,并替换选定区域的文本。
在区域内添加文本
打开 ./src/taskpane/taskpane.html 文件。
查找
change-font
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="insert-text-into-range">Insert Abbreviation</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到change-font
按钮的行,并在该行后添加以下代码。document.getElementById("insert-text-into-range").onclick = () => tryCatch(insertTextIntoRange);
将以下函数添加到文件结尾。
async function insertTextIntoRange() { await Word.run(async (context) => { // TODO1: Queue commands to insert text into a selected range. // TODO2: Load the text of the range and sync so that the // current range text can be read. // TODO3: Queue commands to repeat the text of the original // range at the end of the document. await context.sync(); }); }
在
insertTextIntoRange()
函数中,将TODO1
替换为以下代码。 注意:函数旨在将缩写 [“ (M365) ”] 插入到其文本为“Microsoft 365”的范围的末尾。 它做了一个简化假设,即存在字符串,且用户已选择它。
Range.insertText
方法的第一个参数是要插入到Range
对象的字符串。第二个参数指定了应在区域中的什么位置插入其他文本。 除了“End”外,其他可用选项包括“Start”、“Before”、“After”和“Replace”。
“End”和“After”的区别在于,“End”在现有区域末尾插入新文本,而“After”则是新建包含字符串的区域,并在现有区域后面插入新区域。 同样,“Start”是在现有区域的开头位置插入文本,而“Before”插入的是新区域。 “Replace”将现有区域文本替换为第一个参数中的字符串。
在本教程
insert*
的早期阶段,你看到正文对象的方法没有“Before”和“After”选项。 这是因为不能将内容置于文档正文外。
const doc = context.document; const originalRange = doc.getSelection(); originalRange.insertText(" (M365)", Word.InsertLocation.end);
在下一部分前,将跳过
TODO2
。 在insertTextIntoRange()
函数中,将TODO3
替换为以下代码。 此代码类似于在本教程第一阶段中创建的代码,区别在于现在是要在文档末尾(而不是开头)插入新段落。 这一新段落将说明,新文本现属于原始区域。doc.body.insertParagraph("Original range: " + originalRange.text, Word.InsertLocation.end);
添加代码以将文档属性提取到任务窗格的脚本对象
在本教程之前的所有函数中,你已将命令排队以 写入 Office 文档。 每个函数结束时都会调用 context.sync()
方法,从而将排入队列的命令发送到文档,以供执行。 不过,在上一步中添加的代码调用的是 originalRange.text
属性,这与之前编写的函数明显不同,因为 originalRange
对象只是任务窗格脚本中的代理对象。 由于它并不了解文档中区域的实际文本,因此它的 text
属性无法有实值。 有必要先从文档中提取区域的文本值,再用它设置 originalRange.text
的值。 只有这样才能调用 originalRange.text
,而又不会导致异常抛出。 此获取过程分为三步:
将命令排入队列,以加载 (即提取) 代码需要读取的属性。
调用上下文对象的
sync
方法,从而向文档发送已排入队列的命令以供执行,并返回请求获取的信息。由于
sync
是异步方法,因此请先确保它已完成,然后代码才能调用已提取的属性。
每当代码需要从 Office 文档中 读取 信息时,都必须完成以下步骤。
在
insertTextIntoRange()
函数中,将TODO2
替换为以下代码。originalRange.load("text"); await context.sync();
完成后,整个函数应如下所示:
async function insertTextIntoRange() {
await Word.run(async (context) => {
const doc = context.document;
const originalRange = doc.getSelection();
originalRange.insertText(" (M365)", Word.InsertLocation.end);
originalRange.load("text");
await context.sync();
doc.body.insertParagraph("Original range: " + originalRange.text, Word.InsertLocation.end);
await context.sync();
});
}
在区域间添加文本
打开 ./src/taskpane/taskpane.html 文件。
查找
insert-text-into-range
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="insert-text-outside-range">Add Version Info</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到insert-text-into-range
按钮的行,并在该行后添加以下代码。document.getElementById("insert-text-outside-range").onclick = () => tryCatch(insertTextBeforeRange);
将以下函数添加到文件结尾。
async function insertTextBeforeRange() { await Word.run(async (context) => { // TODO1: Queue commands to insert a new range before the // selected range. // TODO2: Load the text of the original range and sync so that the // range text can be read and inserted. }); }
在
insertTextBeforeRange()
函数中,将TODO1
替换为以下代码。 注意:此函数用于带有文本“Microsoft 365”的区域前添加文本为“Office 2019”的区域。 它假设字符串存在,并且用户已选择该字符串。
Range.insertText
方法的第一个参数是要添加的字符串。第二个参数指定了应在区域中的什么位置插入其他文本。 若要详细了解位置选项,请参阅前面介绍的
insertTextIntoRange
函数。
const doc = context.document; const originalRange = doc.getSelection(); originalRange.insertText("Office 2019, ", Word.InsertLocation.before);
在
insertTextBeforeRange()
函数中,将TODO2
替换为以下代码。originalRange.load("text"); await context.sync(); // TODO3: Queue commands to insert the original range as a // paragraph at the end of the document. // TODO4: Make a final call of context.sync here and ensure // that it runs after the insertParagraph has been queued.
将
TODO3
替换为以下代码。 此新段落将演示新文本 不属于 原始选定范围这一事实。 原始区域中的文本仍与用户选择它时一样。doc.body.insertParagraph("Current text of original range: " + originalRange.text, Word.InsertLocation.end);
将
TODO4
替换为下面的代码。await context.sync();
替换区域文本
打开 ./src/taskpane/taskpane.html 文件。
查找
insert-text-outside-range
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="replace-text">Change Quantity Term</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到insert-text-outside-range
按钮的行,并在该行后添加以下代码。document.getElementById("replace-text").onclick = () => tryCatch(replaceText);
将以下函数添加到文件结尾。
async function replaceText() { await Word.run(async (context) => { // TODO1: Queue commands to replace the text. await context.sync(); }); }
在
replaceText()
函数中,将TODO1
替换为以下代码。 请注意,此函数用于将字符串“几个”替换为字符串“许多”。 它做了一个简化假设,即存在字符串,且用户已选择它。const doc = context.document; const originalRange = doc.getSelection(); originalRange.insertText("many", Word.InsertLocation.replace);
保存对项目所做的所有更改。
测试加载项
如果本地 Web 服务器已在运行,并且加载项已加载到 Word 中,则继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项。
若要在 Word 中测试加载项,请在项目的根目录中运行以下命令。 如果本地 Web 服务器尚未运行) ,则会启动本地 Web 服务器 (,并在加载加载项时打开Word。
npm start
若要在 Word 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将“{url}”替换为你有权访问的 OneDrive 或 SharePoint 库中 Word 文档的 URL。
注意
如果在 Mac 上进行开发,请将 括
{url}
在单引号中。 请勿在 Windows 上执行此操作。npm run start:web -- --document {url}
示例如下。
npm run start:web -- --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版。
如果加载项任务窗格尚未在Word中打开,请转到“开始”选项卡,然后选择功能区上的“显示任务窗格”按钮将其打开。
在任务窗格中,选择“ 插入段落 ”按钮,确保文档开头有一个段落。
在文档中,选择短语“Microsoft 365 订阅”。 请注意不要包含选定区域前的空格和其后的逗号。
选择“插入缩写”按钮。 请注意,添加了“ (M365) ”。 此外,还请观察,文档底部是否添加了包含整个扩展文本的新段落,因为新字符串已添加到现有区域中。
在文档中,选择短语“Microsoft 365”。 请注意不要包含选定区域前后的空格。
选择“添加版本信息”按钮。 观察是否已在“Office 2016”和“Microsoft 365”之间插入“Office 2019”。 此外,还请观察,文档底部是否添加了仅包含最初选定文本的新段落,因为新字符串已变成新区域,而不是添加到原始区域中。
在文档中,选择“几个”一词。 请注意,不要在选定区域的前后添加空格。
选择“更改数量术语”按钮。 观察选定文本是否替换为“多个”。
插入图像、HTML 和表格
本教程的这一步是,了解如何在文档中插入图像、HTML 和表格。
定义图像
完成以下步骤,定义要在本教程的下一部分插入到文档中的图像。
在项目的根目录中,创建一个名为 base64Image.js 的新文件。
打开文件 base64Image.js 并添加以下代码以指定表示图像的 Base64 编码字符串。
export const base64Image = "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";
插入图像
打开 ./src/taskpane/taskpane.html 文件。
查找
replace-text
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="insert-image">Insert Image</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在文件顶部附近找到
Office.onReady
函数调用,然后在该行之前添加以下代码。 此代码将导入你先前在文件 /base64Image.js 中定义的变量。import { base64Image } from "../../base64Image";
在
Office.onReady
函数调用中,定位将单击处理程序分配到replace-text
按钮的行,并在该行后添加以下代码。document.getElementById("insert-image").onclick = () => tryCatch(insertImage);
将以下函数添加到文件结尾。
async function insertImage() { await Word.run(async (context) => { // TODO1: Queue commands to insert an image. await context.sync(); }); }
在
insertImage()
函数中,将TODO1
替换为以下代码。 请注意,此行在文档末尾插入 Base64 编码的图像。 (对象Paragraph
还具有insertInlinePictureFromBase64
方法和其他insert*
方法。有关 example,请参阅以下“插入 HTML”部分。)context.document.body.insertInlinePictureFromBase64(base64Image, Word.InsertLocation.end);
插入 HTML
打开 ./src/taskpane/taskpane.html 文件。
查找
insert-image
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="insert-html">Insert HTML</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到insert-image
按钮的行,并在该行后添加以下代码。document.getElementById("insert-html").onclick = () => tryCatch(insertHTML);
将以下函数添加到文件结尾。
async function insertHTML() { await Word.run(async (context) => { // TODO1: Queue commands to insert a string of HTML. await context.sync(); }); }
在
insertHTML()
函数中,将TODO1
替换为以下代码。 注意:第一行代码在文档末尾添加空白段落。
第二行在段落末尾插入 HTML 字符串;特别是两个段落,一个使用 Verdana 字体设置格式,另一个段落采用Word文档的默认样式。 (如前面的
insertImage
方法一样,context.document.body
对象还包含insert*
方法。)
const blankParagraph = context.document.body.paragraphs.getLast().insertParagraph("", Word.InsertLocation.after); blankParagraph.insertHtml('<p style="font-family: verdana;">Inserted HTML.</p><p>Another paragraph</p>', Word.InsertLocation.end);
插入表格
打开 ./src/taskpane/taskpane.html 文件。
查找
insert-html
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="insert-table">Insert Table</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到insert-html
按钮的行,并在该行后添加以下代码。document.getElementById("insert-table").onclick = () => tryCatch(insertTable);
将以下函数添加到文件结尾。
async function insertTable() { await Word.run(async (context) => { // TODO1: Queue commands to get a reference to the paragraph // that will precede the table. // TODO2: Queue commands to create a table and populate it with data. await context.sync(); }); }
在
insertTable()
函数中,将TODO1
替换为以下代码。 请注意,此行使用ParagraphCollection.getFirst
方法获取对第一段的引用,然后使用Paragraph.getNext
方法获取对第二段的引用。const secondParagraph = context.document.body.paragraphs.getFirst().getNext();
在
insertTable()
函数中,将TODO2
替换为以下代码。 注意:insertTable
方法的前两个参数指定行数和列数。第三个参数指定要在哪里插入表格(在此示例中,是在段落后面插入)。
第四个参数是用于设置表格单元格值的二维数组。
虽然表格采用普通的默认样式,但
insertTable
方法返回的Table
对象包含多个成员,其中部分成员用于设置表格样式。
const tableData = [ ["Name", "ID", "Birth City"], ["Bob", "434", "Chicago"], ["Sue", "719", "Havana"], ]; secondParagraph.insertTable(3, 3, Word.InsertLocation.after, tableData);
保存对项目所做的所有更改。
测试加载项
如果本地 Web 服务器已在运行,并且加载项已加载到 Word 中,则继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项。
若要在 Word 中测试加载项,请在项目的根目录中运行以下命令。 如果本地 Web 服务器尚未运行) ,则会启动本地 Web 服务器 (,并在加载加载项时打开Word。
npm start
若要在 Word 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将“{url}”替换为你有权访问的 OneDrive 或 SharePoint 库中 Word 文档的 URL。
注意
如果在 Mac 上进行开发,请将 括
{url}
在单引号中。 请勿在 Windows 上执行此操作。npm run start:web -- --document {url}
示例如下。
npm run start:web -- --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版。
如果加载项任务窗格尚未在Word中打开,请转到“开始”选项卡,然后选择功能区上的“显示任务窗格”按钮将其打开。
在任务窗格中,至少选择“插入段落”按钮三次,以确保文档中有多个段落。
选择“插入图像”按钮,观察图像是否插入在文档末尾。
选择“ 插入 HTML ”按钮,请注意,文档末尾插入了两个段落,第一个段落具有 Verdana 字体。
选择“插入表格”按钮,观察是否在第二个段落后面插入了表格。
创建和更新内容控件
本教程的这一步是,了解如何在文档中创建格式文本内容控件,以及如何插入和替换控件的内容。
注意
开始执行本教程的这一步之前,建议通过 Word UI 创建和控制格式文本内容控件,以便熟悉此类控件及其属性。 有关详细信息,请参阅在 Word 中创建用户填写或打印的表单。
创建内容控件
打开 ./src/taskpane/taskpane.html 文件。
查找
insert-table
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="create-content-control">Create Content Control</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到insert-table
按钮的行,并在该行后添加以下代码。document.getElementById("create-content-control").onclick = () => tryCatch(createContentControl);
将以下函数添加到文件结尾。
async function createContentControl() { await Word.run(async (context) => { // TODO1: Queue commands to create a content control. await context.sync(); }); }
在
createContentControl()
函数中,将TODO1
替换为以下代码。 注意:此代码旨在将短语“Microsoft 365”包装在内容控件中。 它做了一个简化假设,即存在字符串,且用户已选择它。
ContentControl.title
属性指定内容控件的可见标题。ContentControl.tag
属性指定标记,可用于通过ContentControlCollection.getByTag
方法获取对内容控件的引用,将用于稍后出现的函数。ContentControl.appearance
属性指定控件的外观。 使用值“Tags”表示,控件包装在开始标记和结束标记中,且开始标记包含内容控件标题。 其他可取值包括“BoundingBox”和“None”。ContentControl.color
属性指定标记颜色或边界框的边框。
const serviceNameRange = context.document.getSelection(); const serviceNameContentControl = serviceNameRange.insertContentControl(); serviceNameContentControl.title = "Service Name"; serviceNameContentControl.tag = "serviceName"; serviceNameContentControl.appearance = "Tags"; serviceNameContentControl.color = "blue";
替换内容控件的内容
打开 ./src/taskpane/taskpane.html 文件。
查找
create-content-control
按钮的<button>
元素,并在行后添加下列标记。<button class="ms-Button" id="replace-content-in-control">Rename Service</button><br/><br/>
打开 ./src/taskpane/taskpane.js 文件。
在
Office.onReady
函数调用中,定位将单击处理程序分配到create-content-control
按钮的行,并在该行后添加以下代码。document.getElementById("replace-content-in-control").onclick = () => tryCatch(replaceContentInControl);
将以下函数添加到文件结尾。
async function replaceContentInControl() { await Word.run(async (context) => { // TODO1: Queue commands to replace the text in the Service Name // content control. await context.sync(); }); }
在
replaceContentInControl()
函数中,将TODO1
替换为以下代码。 注意:-
ContentControlCollection.getByTag
方法将返回指定标记的所有内容控件的ContentControlCollection
。 我们使用getFirst
来获取对所需控件的引用。
const serviceNameContentControl = context.document.contentControls.getByTag("serviceName").getFirst(); serviceNameContentControl.insertText("Fabrikam Online Productivity Suite", Word.InsertLocation.replace);
-
保存对项目所做的所有更改。
测试加载项
如果本地 Web 服务器已在运行,并且加载项已加载到 Word 中,则继续执行步骤 2。 否则,启动本地 Web 服务器并旁加载你的加载项。
若要在 Word 中测试加载项,请在项目的根目录中运行以下命令。 如果本地 Web 服务器尚未运行) ,则会启动本地 Web 服务器 (,并在加载加载项时打开Word。
npm start
若要在 Word 网页版中测试加载项,请在项目的根目录中运行以下命令。 运行此命令时,本地 Web 服务器将启动。 将“{url}”替换为你有权访问的 OneDrive 或 SharePoint 库中 Word 文档的 URL。
注意
如果在 Mac 上进行开发,请将 括
{url}
在单引号中。 请勿在 Windows 上执行此操作。npm run start:web -- --document {url}
示例如下。
npm run start:web -- --document https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?e=RSccmNP
如果外接程序未在文档中旁加载,请按照手动旁加载加载项中的说明手动旁加载到Office web 版。
如果加载项任务窗格尚未在Word中打开,请转到“开始”选项卡,然后选择功能区上的“显示任务窗格”按钮将其打开。
在任务窗格中,选择“ 插入段落 ”按钮,确保文档顶部有一个带有“Microsoft 365”的段落。
在文档中,选择文本“Microsoft 365”,然后选择“创建内容控件”按钮。 观察此短语是否包装在标签为“服务名称”的标记中。
选择“重命名服务”按钮,并观察内容控件的文本是否变成“Fabrikam Online Productivity Suite”。
后续步骤
在本教程中,你已创建 Word 任务窗格加载项,用于在 Word 文档中插入和替换文本、图像和其他内容。 若要了解有关构建 Word 加载项的详细信息,请继续阅读以下文章。
代码示例
- 已完成Word加载项教程:完成本教程的结果。