当前位置: 首页 > news >正文

在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别标题级别和目录

        我们希望通过AI,能够自动识别像“一”、“(一)”、“1”、“(1)” 这类常见标题序号。做一个规则,如果存在“一”时,则“一”、“(一)”、“1”分别识别为H1、H2、H3;如果不存在“一”时,“(一)”、“1”、“(1)”分别识别为H1、H2、H3;如果不存在“(一)”时,“1”、“(1)”、“1)”分别识别为H1、H2、H3;依次类推。并生成目录,为文档编排减轻负担。

一、需求分析

1.自定义+AI模式

当然,AI也并不是总能够识别精准,所以为了更好的实现,我们做两套规则,即:

1自定义规则识别标题:在代码里添加自定义规则,按照常见标题序号格式识别标题,然后设置对应的标题级别。

2deepseek API识别的规则。

把API返回的结果和自定义规则结合起来,提高标题识别的准确性。

2.具体事项实现思路

(1)判断特定标题序号的存在情况:通过一次遍历文档段落,使用正则表达式判断是否存在 “一、”、“(一)”、“1、” 等标题序号,并将结果存储在相应的布尔变量中。

(2)根据存在情况设置标题级别:再次遍历文档段落,根据之前判断的结果,按照不同的规则将标题序号对应的段落添加到 headings 对象的不同级别中。

(3)API结果处理:调用DeepSeek API来识别标题,把结果存储在apiHeadings里。

(4)结果合并:将 API 返回的标题信息apiHeadings 和自定义规则识别的标题信息headings 合并,然后根据合并后的结果allHeadings 设置文档段落的标题样式。

(5)设置标题级别:依据 allHeadings 里的标题信息,设置文档中对应段落的标题级别。

(6)创建目录:设置好标题级别后,创建并更新目录。

3.标题序号处理

关于标题序号,除了“一、”、“(一)”、“1、”这些类型外,还有序列要实现:'一', '二', '三', '四', '五', '六', '七', '八', '九', '十',所以也要分别做处理:

(1)统计标题序号类型数量:

在判断是否存在不同标题序号(hasOne、hasBracketOne、hasNumberOne)之后,统计存在的标题序号类型的数量,存储在 titleTypeCount 变量中。

(2)处理只有一种标题序号类型的情况:

如果 titleTypeCount 大于 1,说明存在多种标题序号类型,按照之前的规则进行标题级别设置。

如果 titleTypeCount 等于 1,说明只有一种标题序号类型,将所有匹配该标题序号的段落都设置为 标题1。

4.文档信息读取

获取文档段落:使用 doc.Content.Paragraphs 获取文档中的所有段落

获取WPS内置标题样式:使用doc.Styles获取WPS标题样式

5.文档处于受保护状态

若文档处于受保护状态,就无法创建新样式。你需要检查文档是否被保护,若被保护则解除保护。可以按照以下步骤在 WPS 中解除文档保护:

(1)打开文档:在 WPS 中打开你要处理的文档。

(2)找到 “审阅” 选项卡:在 WPS 文字的顶部菜单栏中,找到并点击 “审阅” 选项卡。

(3)点击 “限制编辑”:在 “审阅” 选项卡中,找到 “限制编辑” 按钮,点击它。这通常会在右侧弹出一个 “限制编辑” 的侧边栏。

(4)停止保护文档:在 “限制编辑” 侧边栏中,找到 “停止保护” 按钮并点击。

(5)输入密码(如果有):如果文档设置了保护密码,系统会提示你输入密码。输入正确的密码后,点击 “确定” 按钮。

(6)确认解除保护:完成上述步骤后,文档的保护状态应该就被解除了。你可以再次运行宏代码来检查是否能够成功执行。

解除保护后,再运行之前的宏代码,应该就可以正常创建样式并执行后续的标题识别和目录创建操作了。

二、自定义规则实现

1打开WPS的JS宏编辑器

具体操作可以看我的CSDN文章:在WPS中通过JavaScript宏(JSA)调用DeepSeek官网API优化文档教程-CSDN博客。

【工具】选项卡 --> 【开发工具】 --> 【切换到JS环境】 --> 【WPS宏编辑器】。

2.在Project(Normal.dotm)中新建模块

Project(Normal.dotm)可以控制全局,即所有文档都可以用

在模块中添加以下代码(加入当前的模块名是Module2):

function identifyAndSetHeadingsAndCreateTOC() {// 获取文档内容var doc = this.Application.ActiveDocument;// 尝试获取文档的保护类型,若抛出异常则认为文档可能存在问题try {var protectionType = doc.ProtectionType;if (protectionType!== -1) {alert("文档处于受保护状态,请先解除保护。");return;}} catch (error) {alert("获取文档保护状态时出现异常,请检查文档是否正常。");return;}var paragraphs = doc.Content.Paragraphs;// var title1Style = doc.Styles("标题 1");//    if (title1Style) {//        alert("成功读取标题 1 样式");//    }// 定义标题级别映射var headings = {"标题1": [],"标题2": [],"标题3": []};// 先判断文档中是否存在不同类型的标题序号var hasOne = false;var hasBracketOne = false;var hasNumberOne = false;var hasChineseNumbers = [];var hasBracketNumbers = [];var hasPlainNumbers = [];for (var i = 1; i <= 10; i++) {hasChineseNumbers[i] = false;hasBracketNumbers[i] = false;hasPlainNumbers[i] = false;}for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);var text = para.Range.Text.trim();if (text) {for (var j = 1; j <= 10; j++) {var chineseRegex = new RegExp(`^${getChineseNumber(j)}、|^${getChineseNumber(j)}\\.|^${getChineseNumber(j)}\\s`);var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);if (chineseRegex.test(text)) {if (j === 1) hasOne = true;hasChineseNumbers[j] = true;} else if (bracketRegex.test(text)) {if (j === 1) hasBracketOne = true;hasBracketNumbers[j] = true;} else if (plainRegex.test(text)) {if (j === 1) hasNumberOne = true;hasPlainNumbers[j] = true;}}}}// 统计存在的标题序号类型数量var titleTypeCount = 0;for (var i = 1; i <= 10; i++) {if (hasChineseNumbers[i]) titleTypeCount++;if (hasBracketNumbers[i]) titleTypeCount++;if (hasPlainNumbers[i]) titleTypeCount++;}// 根据存在情况设置标题级别for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);var text = para.Range.Text.trim();if (text) {if (titleTypeCount > 1) {if (hasOne) {for (var j = 1; j <= 10; j++) {var chineseRegex = new RegExp(`^${getChineseNumber(j)}、|^${getChineseNumber(j)}\\.|^${getChineseNumber(j)}\\s`);var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);if (chineseRegex.test(text)) {headings["标题1"].push(text);} else if (bracketRegex.test(text)) {headings["标题2"].push(text);} else if (plainRegex.test(text)) {headings["标题3"].push(text);}}} else if (hasBracketOne) {for (var j = 1; j <= 10; j++) {var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);var subBracketRegex = new RegExp(`^(${j})`);if (bracketRegex.test(text)) {headings["标题1"].push(text);} else if (plainRegex.test(text)) {headings["标题2"].push(text);} else if (subBracketRegex.test(text)) {headings["标题3"].push(text);}}} else if (hasNumberOne) {for (var j = 1; j <= 10; j++) {var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);var subBracketRegex = new RegExp(`^(${j})`);var subPlainRegex = new RegExp(`^${j})`);if (plainRegex.test(text)) {headings["标题1"].push(text);} else if (subBracketRegex.test(text)) {headings["标题2"].push(text);} else if (subPlainRegex.test(text)) {headings["标题3"].push(text);}}}} else {// 只有一种标题序号类型,全部设为标题1for (var j = 1; j <= 10; j++) {var chineseRegex = new RegExp(`^${getChineseNumber(j)}、|^${getChineseNumber(j)}\\.|^${getChineseNumber(j)}\\s`);var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);var subBracketRegex = new RegExp(`^(${j})`);var subPlainRegex = new RegExp(`^${j})`);if (chineseRegex.test(text) || bracketRegex.test(text) || plainRegex.test(text) || subBracketRegex.test(text) || subPlainRegex.test(text)) {headings["标题1"].push(text);}}}}}// 检查样式是否存在if (!doc.Styles("标题 1") || !doc.Styles("标题 2") || !doc.Styles("标题 3")) {alert("样式 标题 1、标题 2或标题 3不存在,请检查!");return;}// 设置标题级别for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);var text = para.Range.Text.trim();if (headings["标题1"].includes(text)) {try {para.Style = doc.Styles("标题 1");} catch (e) {alert(`设置“标题 1”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}} else if (headings["标题2"].includes(text)) {try {para.Style = doc.Styles("标题 2");} catch (e) {alert(`设置“标题 2”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}} else if (headings["标题3"].includes(text)) {try {para.Style = doc.Styles("标题 3");} catch (e) {alert(`设置“标题 3”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}}}// 创建目录var tocPara = doc.Content.Paragraphs.Add();var tocRange = tocPara.Range;tocRange.InsertAfter("目录\n");try {var toc = doc.TablesOfContents.Add(tocRange, {"UseHeadingStyles": true,"UpperHeadingLevel": 1,"LowerHeadingLevel": 3});toc.Update();alert('设置标题并识别目录,完成!');} catch (e) {alert("创建目录失败,请检查标题设置!");}}function getChineseNumber(num) {var chineseNumbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];return chineseNumbers[num - 1];}

界面效果如下:

接下来即可点击界面上的“运行”按钮,直至提示完成,即可看文档被设置后的效果!

三、结合DeepSeek的API能力

要在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别文档的标题级别并设置,同时实现识别并创建目录,可以按照以下步骤进行:

1. 获取 DeepSeek API 密钥

首先,你需要前往DeepSeek官网注册账号,在开发者中心创建应用,从而获取API Key。注意选择合适的模型,如deepseek - v3。

具体的内容同样可以看我的CSDN文章:在WPS中通过JavaScript宏(JSA)调用DeepSeek官网API优化文档教程-CSDN博客。

2. 编写 WPS JavaScript 宏代码

在Module2中继续添加代码,添加在identifyAndSetHeadingsAndCreateTOC()函数中,添加的位置如下,插入在“检查样式是否存在”之前

// 根据存在情况设置标题级别

    for (var i = 1; i <= paragraphs.Count; i++) {}

------ 添加在此位置 ------

// 检查样式是否存在

    if (!doc.Styles("标题 1") || !doc.Styles("标题 2") || !doc.Styles("标题 3")) {

        alert("样式 标题 1、标题 2或标题 3不存在,请检查!");

        return;

    }

以下是添加的详细代码:

    // 显示正在执行的提示alert("正在调用 DeepSeek API 进行标题识别,请稍候...\n点击确定,将进入后台运行!");// 尝试调用 DeepSeek API 辅助识别标题var apiHeadings = {"标题1": [],"标题2": [],"标题3": []};try {var content = "";for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);content += para.Range.Text;}// DeepSeek API 配置var apiUrl = 'https://api.deepseek.com/v1/chat/completions';var apiKey = 'sk-e4df3c51537048af980934467b594163';var model = 'deepseek-chat';// 构建请求体,明确告知 API 识别标题序号并分类var requestBody = {"model": model,"messages": [{"role": "user","content": `请识别以下文档内容中以 "一、"或"一."或"一 "、"(一)"、"1、"或"1."或"1 "、"(1)"、"1)" 等开头的标题,并按照3个级别进行分类,格式为:标题1: [标题1内容1, 标题1内容2, ...]标题2: [标题2内容1, 标题2内容2, ...]标题3: [标题3内容1, 标题3内容2, ...]${content}`}],"stream": false};requestBody = JSON.stringify(requestBody);// 创建 XMLHttpRequest 对象var xhr = new XMLHttpRequest();xhr.open('POST', apiUrl, false);// 设置请求头xhr.setRequestHeader('Content-Type', 'application/json');xhr.setRequestHeader('Authorization', 'Bearer ' + apiKey);// 设置超时时间(单位:毫秒)xhr.timeout = 30000;// 超时处理函数xhr.ontimeout = function () {alert('请求超时,请稍后重试!');return;};// 发送请求xhr.send(requestBody);if (xhr.status === 200) {var response = JSON.parse(xhr.responseText);var result = response.choices[0].message.content;// 解析 API 结果var lines = result.split('\n');for (var i = 0; i < lines.length; i++) {var line = lines[i];if (line.startsWith('标题1:')) {var titles = line.substring(4).trim().replace('[', '').replace(']', '').split(',');for (var j = 0; j < titles.length; j++) {apiHeadings["标题1"].push(titles[j].trim());}} else if (line.startsWith('标题2:')) {var titles = line.substring(4).trim().replace('[', '').replace(']', '').split(',');for (var j = 0; j < titles.length; j++) {apiHeadings["标题2"].push(titles[j].trim());}} else if (line.startsWith('标题3:')) {var titles = line.substring(4).trim().replace('[', '').replace(']', '').split(',');for (var j = 0; j < titles.length; j++) {apiHeadings["标题3"].push(titles[j].trim());}}}} else {alert('请求失败,状态码:' + xhr.status);return;}} catch (e) {alert('调用 DeepSeek API 出错:' + e.message);return;}if(headings["标题1"].length!=0){if(apiHeadings["标题1"].length==0){alert("请继续等待30秒...");}}// 合并自定义规则和 API 结果var allHeadings = {"标题1": headings["标题1"].concat(apiHeadings["标题1"]),"标题2": headings["标题2"].concat(apiHeadings["标题2"]),"标题3": headings["标题3"].concat(apiHeadings["标题3"])};// 隐藏正在执行的提示(这里只是逻辑上的隐藏,alert 无法真正隐藏)// 可以考虑使用更复杂的 UI 组件来实现显示和隐藏添加完之后,修改以下代码段为(其实就是将代码中的headings改为allHeadings):// 设置标题级别for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);var text = para.Range.Text.trim();if (allHeadings["标题1"].includes(text)) {try {para.Style = doc.Styles("标题 1");} catch (e) {alert(`设置“标题 1”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}} else if (allHeadings["标题2"].includes(text)) {try {para.Style = doc.Styles("标题 2");} catch (e) {alert(`设置“标题 2”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}} else if (allHeadings["标题3"].includes(text)) {try {para.Style = doc.Styles("标题 3");} catch (e) {alert(`设置“标题 3”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}}}

至此代码就完成了!

3. 新建WPS组件

这个方式在我的另外一篇文章中已经讲解过了,具体的可以参照CSDN文章:在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程:在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程_wps调用api-CSDN博客。

(1)创建新选项卡和组。

前文已经创建过了DeepSeek选项卡。我们继续在这个选项卡下新增。

(2)新建组

前文我们也已经添加了“AI优化”组件。接下来我们再添加一个“创建标题与目录”的组。如下图:

(3)为“创建标题与目录”组添加组件

选中“创建标题与目录”组,在左侧下来选择“宏”,选择identifyAndSetHeadingsAndCreateTOC,之后点击“添加”,将组件添加到“创建标题与目录”下。

(4)组件改名

对Project.Module2.identifyAndSetHeadingsAndCreateTOC改名:创建标题与目录。可得如下效果:

4. 注意事项

(1)请将代码中的 sk-替换为你的API密钥 替换为你实际的 DeepSeek API 密钥。

(2)确保你的网络连接正常,因为需要调用 DeepSeek API。

(3)该代码依赖于 WPS 的内置标题样式 “标题 1”、“标题 2” 和 “标题 3”,请确保文档中这些样式存在。

当然代码还有很多不完善的,逻辑结构考虑的不够完整,大家可以继续修改!

5. 附上identifyAndSetHeadingsAndCreateTOC完整代码

function identifyAndSetHeadingsAndCreateTOC() {// 获取文档内容var doc = this.Application.ActiveDocument;// 尝试获取文档的保护类型,若抛出异常则认为文档可能存在问题try {var protectionType = doc.ProtectionType;if (protectionType!== -1) {alert("文档处于受保护状态,请先解除保护。");return;}} catch (error) {alert("获取文档保护状态时出现异常,请检查文档是否正常。");return;}var paragraphs = doc.Content.Paragraphs;
//	var title1Style = doc.Styles("标题 1");
//    if (title1Style) {
//        alert("成功读取标题 1 样式");
//    }// 定义标题级别映射var headings = {"标题1": [],"标题2": [],"标题3": []};// 先判断文档中是否存在不同类型的标题序号var hasOne = false;var hasBracketOne = false;var hasNumberOne = false;var hasChineseNumbers = [];var hasBracketNumbers = [];var hasPlainNumbers = [];for (var i = 1; i <= 10; i++) {hasChineseNumbers[i] = false;hasBracketNumbers[i] = false;hasPlainNumbers[i] = false;}for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);var text = para.Range.Text.trim();if (text) {for (var j = 1; j <= 10; j++) {var chineseRegex = new RegExp(`^${getChineseNumber(j)}、|^${getChineseNumber(j)}\\.|^${getChineseNumber(j)}\\s`);var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);if (chineseRegex.test(text)) {if (j === 1) hasOne = true;hasChineseNumbers[j] = true;} else if (bracketRegex.test(text)) {if (j === 1) hasBracketOne = true;hasBracketNumbers[j] = true;} else if (plainRegex.test(text)) {if (j === 1) hasNumberOne = true;hasPlainNumbers[j] = true;}}}}// 统计存在的标题序号类型数量var titleTypeCount = 0;for (var i = 1; i <= 10; i++) {if (hasChineseNumbers[i]) titleTypeCount++;if (hasBracketNumbers[i]) titleTypeCount++;if (hasPlainNumbers[i]) titleTypeCount++;}// 根据存在情况设置标题级别for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);var text = para.Range.Text.trim();if (text) {if (titleTypeCount > 1) {if (hasOne) {for (var j = 1; j <= 10; j++) {var chineseRegex = new RegExp(`^${getChineseNumber(j)}、|^${getChineseNumber(j)}\\.|^${getChineseNumber(j)}\\s`);var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);if (chineseRegex.test(text)) {headings["标题1"].push(text);} else if (bracketRegex.test(text)) {headings["标题2"].push(text);} else if (plainRegex.test(text)) {headings["标题3"].push(text);}}} else if (hasBracketOne) {for (var j = 1; j <= 10; j++) {var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);var subBracketRegex = new RegExp(`^(${j})`);if (bracketRegex.test(text)) {headings["标题1"].push(text);} else if (plainRegex.test(text)) {headings["标题2"].push(text);} else if (subBracketRegex.test(text)) {headings["标题3"].push(text);}}} else if (hasNumberOne) {for (var j = 1; j <= 10; j++) {var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);var subBracketRegex = new RegExp(`^(${j})`);var subPlainRegex = new RegExp(`^${j})`);if (plainRegex.test(text)) {headings["标题1"].push(text);} else if (subBracketRegex.test(text)) {headings["标题2"].push(text);} else if (subPlainRegex.test(text)) {headings["标题3"].push(text);}}}} else {// 只有一种标题序号类型,全部设为标题1for (var j = 1; j <= 10; j++) {var chineseRegex = new RegExp(`^${getChineseNumber(j)}、|^${getChineseNumber(j)}\\.|^${getChineseNumber(j)}\\s`);var bracketRegex = new RegExp(`^(${j})`);var plainRegex = new RegExp(`^${j}、|^${j}\\.|^${j}\\s`);var subBracketRegex = new RegExp(`^(${j})`);var subPlainRegex = new RegExp(`^${j})`);if (chineseRegex.test(text) || bracketRegex.test(text) || plainRegex.test(text) || subBracketRegex.test(text) || subPlainRegex.test(text)) {headings["标题1"].push(text);}}}}}// 显示正在执行的提示alert("正在调用 DeepSeek API 进行标题识别,请稍候...\n点击确定,将进入后台运行!");// 尝试调用 DeepSeek API 辅助识别标题var apiHeadings = {"标题1": [],"标题2": [],"标题3": []};try {var content = "";for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);content += para.Range.Text;}// DeepSeek API 配置var apiUrl = 'https://api.deepseek.com/v1/chat/completions';var apiKey = 'sk-e4df3c51537048af980934467b594163';var model = 'deepseek-chat';// 构建请求体,明确告知 API 识别标题序号并分类var requestBody = {"model": model,"messages": [{"role": "user","content": `请识别以下文档内容中以 "一、"或"一."或"一 "、"(一)"、"1、"或"1."或"1 "、"(1)"、"1)" 等开头的标题,并按照3个级别进行分类,格式为:
标题1: [标题1内容1, 标题1内容2, ...]
标题2: [标题2内容1, 标题2内容2, ...]
标题3: [标题3内容1, 标题3内容2, ...]${content}`}],"stream": false};requestBody = JSON.stringify(requestBody);// 创建 XMLHttpRequest 对象var xhr = new XMLHttpRequest();xhr.open('POST', apiUrl, false);// 设置请求头xhr.setRequestHeader('Content-Type', 'application/json');xhr.setRequestHeader('Authorization', 'Bearer ' + apiKey);// 设置超时时间(单位:毫秒)xhr.timeout = 30000;// 超时处理函数xhr.ontimeout = function () {alert('请求超时,请稍后重试!');return;};// 发送请求xhr.send(requestBody);if (xhr.status === 200) {var response = JSON.parse(xhr.responseText);var result = response.choices[0].message.content;// 解析 API 结果var lines = result.split('\n');for (var i = 0; i < lines.length; i++) {var line = lines[i];if (line.startsWith('标题1:')) {var titles = line.substring(4).trim().replace('[', '').replace(']', '').split(',');for (var j = 0; j < titles.length; j++) {apiHeadings["标题1"].push(titles[j].trim());}} else if (line.startsWith('标题2:')) {var titles = line.substring(4).trim().replace('[', '').replace(']', '').split(',');for (var j = 0; j < titles.length; j++) {apiHeadings["标题2"].push(titles[j].trim());}} else if (line.startsWith('标题3:')) {var titles = line.substring(4).trim().replace('[', '').replace(']', '').split(',');for (var j = 0; j < titles.length; j++) {apiHeadings["标题3"].push(titles[j].trim());}}}} else {alert('请求失败,状态码:' + xhr.status);return;}} catch (e) {alert('调用 DeepSeek API 出错:' + e.message);return;}if(headings["标题1"].length!=0){if(apiHeadings["标题1"].length==0){alert("请继续等待30秒...");}}// 合并自定义规则和 API 结果var allHeadings = {"标题1": headings["标题1"].concat(apiHeadings["标题1"]),"标题2": headings["标题2"].concat(apiHeadings["标题2"]),"标题3": headings["标题3"].concat(apiHeadings["标题3"])};// 隐藏正在执行的提示(这里只是逻辑上的隐藏,alert 无法真正隐藏)// 可以考虑使用更复杂的 UI 组件来实现显示和隐藏// 检查样式是否存在if (!doc.Styles("标题 1") || !doc.Styles("标题 2") || !doc.Styles("标题 3")) {alert("样式 标题 1、标题 2或标题 3不存在,请检查!");return;}// 设置标题级别for (var i = 1; i <= paragraphs.Count; i++) {var para = paragraphs.Item(i);var text = para.Range.Text.trim();if (allHeadings["标题1"].includes(text)) {try {para.Style = doc.Styles("标题 1");} catch (e) {alert(`设置“标题 1”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}} else if (allHeadings["标题2"].includes(text)) {try {para.Style = doc.Styles("标题 2");} catch (e) {alert(`设置“标题 2”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}} else if (allHeadings["标题3"].includes(text)) {try {para.Style = doc.Styles("标题 3");} catch (e) {alert(`设置“标题 3”样式时出现意外错误,段落内容:${text},错误信息:${e.message}`);}}}// 创建目录var tocPara = doc.Content.Paragraphs.Add();var tocRange = tocPara.Range;tocRange.InsertAfter("目录\n");try {var toc = doc.TablesOfContents.Add(tocRange, {"UseHeadingStyles": true,"UpperHeadingLevel": 1,"LowerHeadingLevel": 3});toc.Update();alert('设置标题并识别目录,完成!');} catch (e) {alert("创建目录失败,请检查标题设置!");}
}function getChineseNumber(num) {var chineseNumbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];return chineseNumbers[num - 1];
}

相关文章:

在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别标题级别和目录

我们希望通过AI&#xff0c;能够自动识别像“一”、“&#xff08;一&#xff09;”、“1”、“&#xff08;1&#xff09;” 这类常见标题序号。做一个规则&#xff0c;如果存在“一”时&#xff0c;则“一”、“&#xff08;一&#xff09;”、“1”分别识别为H1、H2、H3&…...

修复 WPS 编译错误:缺少:Sub或Function 且出现两个MathType加载项

问题首次出现于2025.4.12。 同时使用了Word和WPS&#xff0c;在里面都使用了MathType&#xff0c;在Microsoft的Word中&#xff0c;加载项能正常加载且显示&#xff0c;这也是我们要的效果。 而在WPS中&#xff0c;却出现了两个MathType&#xff0c;且在启动时会弹窗报错&…...

HTTP协议

目录 1 Fiddler工具 2 HTTP协议 2.1 HTTP请求和响应格式 2.2 URL 2.3 方法method 2.3.1 GET 2.3.2 POST 2.4 请求报头Header 2.5 请求正文body 2.6 http响应状态码 2.7 响应报头header HTTP协议是应用层的协议&#xff0c;基于传输层的TCP协议来传输&#xff0c;数据…...

拓扑排序 —— 2. 力扣刷题207. 课程表

题目链接&#xff1a;https://leetcode.cn/problems/course-schedule/description/ 题目难度&#xff1a;中等 相关标签&#xff1a;拓扑排序 / 广度优先搜搜 BFS / 深度优先搜索 DFS 2.1 问题与分析 2.1.1 原题截图 2.1.2 题目分析 首先&#xff0c;理解题目后必须马上意识到…...

寻找峰值 --- 二分查找

目录 一&#xff1a;题目 二&#xff1a;算法原理 三&#xff1a;代码实现 一&#xff1a;题目 题目链接&#xff1a;162. 寻找峰值 - 力扣&#xff08;LeetCode&#xff09; 二&#xff1a;算法原理 三&#xff1a;代码实现 class Solution { public:int findPeakElemen…...

SAP系统客户可回收包材库存管理

问题&#xff1a;客户可回收包材库存管理 现象&#xff1a;回收瓶无库存管理&#xff0c;在库数量以及在客户的库存数量没有统计&#xff0c;管理混乱。 解决方法&#xff1a; 客户可回收包装材料在SAP有标准的解决方案&#xff0c;在集团尚未启用该业务&#xff0c;首先…...

C++标识符:检查是否和保留字冲突

1. 基础知识 最基本的要求&#xff1a; 字母、数字、下划线组成&#xff0c; 并且不能是数字开头。 禁忌1&#xff1a; C 关键字不能用做标识符。 它们是&#xff1a; alignas alignof asm auto bool break case catch char char16_t char32_t class const constexpr const_…...

【Java多线程】告别线程混乱!深度解析Java多线程4大实现方式(附实战案例)

一、继承Thread类 实现步骤&#xff1a; 1.继承Thread类 2.重写run()方法 3.创建线程对象并调用start()方法 示例&#xff1a; class MyThread extends Thread {Overridepublic void run() {for (int i 0; i < 5; i) {System.out.println(Thread.currentThread().getNam…...

Linux安装yum和python

一、安装yum(CentOS) 查看yum版本 yum --version 如果未安装&#xff0c;执行以下部分&#xff1a; 1. 确保你的系统中已经安装了epel仓库&#xff0c;如果没有安装可以通过以下命令安装&#xff1a; sudo yum install epel-release 2.yum安装 – CentOS/RHEL系统&#…...

【数据结构】HashMap源码 —— 简单介绍

HashMap源码介绍 下面并非完整的源码&#xff0c;主要简单了解其流程。 1. 基本成员变量 哈希桶/开散列&#xff0c;链地址法/开链法是由&#xff1a;数组 链表(单链表) 红黑树&#xff08;当数组长度>64 && 链表长度>8以后&#xff0c;链表变成红黑树&#xf…...

149页研读——华为基于IPD全过程研发质量管理【附全文阅读】

本文介绍了IPD(集成产品开发)的全过程研发质量管理,强调了以客户需求为导向,通过跨部门协同、资源整合、快速响应等方式提高研发效率和成功率。文章详细阐述了IPD研发管理体系的精要,包括其核心思想、优势、框架以及核心理念。 其中,跨领域平台与技术研发、端到端流程与项…...

深入理解 v-for 指令及其使用方法

在 Vue.js 中&#xff0c;v-for 是用于渲染列表的核心指令&#xff0c;它允许你通过循环渲染数据源中的每一项。通过 v-for&#xff0c;你可以轻松地将数组、对象或其他可迭代的数据渲染成 HTML 元素。本文将详细介绍 v-for 的基本用法、常见的应用场景、最佳实践及性能优化&am…...

swift菜鸟教程24-25(可选链,自动引用计数)

一个朴实无华的目录 今日学习内容&#xff1a;1.Swift 可选链1.1定义1.2通过可选链调用方法1.3使用可选链调用下标脚本1.4通过可选链接调用来访问下标1.4访问可选类型的下标 2.Swift 自动引用计数&#xff08;ARC&#xff09;2.1实例之间的循环强引用会造成内存泄露2.2弱引用&a…...

使用 Visual Studio 2022 (VS2022) 编译 FreeCAD 1.0.0 的详细教程

一、环境准备 官方教程&#xff1a;在 Windows 上编译 - FreeCAD Documentation Windows 10/11&#xff08;推荐&#xff09; git vs2022 cmake 3.26.4 Doxygen1.12 二、获取源码与依赖 版本关系 打开Git Bash或CMD&#xff0c;执行以下命令 git clone --recurse-sub…...

机械臂只有位置信息是否可以进行手眼标定?

平常我在做手眼标定时&#xff0c;一般都是通过OpenCV的cv::calibrateHandEye函数进行求解&#xff0c;需要输入多组不同的机械臂位姿。今天遇到了一款舵机机器人&#xff0c;只能获取位置&#xff0c;得不到姿态信息&#xff0c;想着那就把姿态都设为0&#xff0c;结果求不出来…...

Unity入门

文章目录 Unity脚本基础大基础生命周期函数Inspector窗口显示常用特性辅助特性 MnonBehaviour基类成员变量成员方法 组件GameObject成员变量gameObject静态方法成员方法 组件Time静态成员变量 组件TransformVector3结构体基础基本概念常用向量表示常用方法 位置与位移位置posit…...

《汽车制造技术基础》第一次作业

作业内容 查阅相关资料&#xff0c;谈谈对汽车制造技术的发展的理解。 可以是关于汽车的先进制造技术 或 汽车先进制造技术 与 制造理念的发展趋势 或 汽车先进制造技术对环境与可持续发展的影响等。 以下从技术突破、制造理念转型及环境影响三个维度展开对汽车制造技…...

烟花爆竹储存作业安全要求

烟花爆竹储存作业证是从事相关作业的法定凭证&#xff0c;旨在确保操作人员具备专业知识和安全技能&#xff0c;防止因违规操作引发火灾、爆炸等事故。根据《烟花爆竹安全管理条例》及相关法规&#xff0c;未取得作业证的人员不得从事烟花爆竹储存、搬运、管理等作业。 仓库选址…...

Flask+Plotly结合动态加载图形页面实践

1. DeepSeek帮我实践 1.1. 我的提问既设计方案 原有如下主页:dashboard.html,现增加“预测模型学习”,对感知机神经网络描述如下: 1、输入与输出为固定值,例如输入层215,输出层48; 2、模型为回归神经网络; 3、中层是可动态调整的,例如定义如下:第二层,200,第三层…...

leetcode每日一题:统计好整数的数目

题目 给你两个 正 整数 n 和 k 。 如果一个整数 x 满足以下条件&#xff0c;那么它被称为 k 回文 整数 。 x 是一个 回文整数 。 x 能被 k 整除。 如果一个整数的数位重新排列后能得到一个 k 回文整数 &#xff0c;那么我们称这个整数为 好 整数。比方说&#xff0c;k 2 …...

《2025蓝桥杯C++B组:D:产值调整》

**作者的个人gitee**​​ 作者的算法讲解主页▶️ 每日一言&#xff1a;“泪眼问花花不语&#xff0c;乱红飞过秋千去&#x1f338;&#x1f338;” 题目 二.解题策略 本题比较简单&#xff0c;我的思路是写三个函数分别计算黄金白银铜一次新产值&#xff0c;通过k次循环即可获…...

【模块化拆解与多视角信息1】基础信息:隐藏的筛选规则——那些简历上没说出口的暗号

写在最前 作为一个中古程序猿,我有很多自己想做的事情,比如埋头苦干手搓一个低代码数据库设计平台(目前只针对写java的朋友),比如很喜欢帮身边的朋友看看简历,讲讲面试技巧,毕竟工作这么多年,也做到过高管,有很多面人经历,意见还算有用,大家基本都能拿到想要的offe…...

当Browser Use遇见A2A:浏览器自动化与智能体协作的“冰与火之歌“

——一场正在改写数字文明的技术奇遇 第一章 浏览器革命&#xff1a;从"手动挡"到"自动驾驶" 1.1 传统自动化工具的"中年危机" 还记得2023年那个抓狂的凌晨吗&#xff1f;你蹲守演唱会门票时&#xff0c;Selenium脚本因为验证码识别失败第108次…...

Python:开启自动化办公与游戏开发的无限可能

重要的事情放在前面 Python自动化办公和游戏 Python&#xff1a;开启自动化办公与游戏开发的无限可能 在数字化时代的浪潮中&#xff0c;Python以其强大的功能和简洁的语法&#xff0c;成为了众多开发者手中的得力工具&#xff0c;尤其在自动化办公与游戏开发领域&#xff0…...

腾讯后台开发 一面

一、手撕 合并升序链表 合并两个排序的链表_牛客题霸_牛客网 顺时针翻转矩阵 顺时针旋转矩阵_牛客题霸_牛客网 二、八股 1、静态变量和实例变量 public class House {public static String buildDate "2024-10-27"; // 静态变量public String color; // 实…...

基于生成对抗网络(GAN)的手写数字生成实践

基于生成对抗网络&#xff08;GAN&#xff09;的手写数字生成实践 一、图像生成的技术演进 在人工智能领域&#xff0c;图像生成技术经历了从传统算法到深度学习的革命性发展。其中&#xff0c;生成对抗网络&#xff08;Generative Adversarial Networks, GANs&#xff09;作…...

网络互连与互联网

1.在路由表中找不到目标网络时使用默认路由&#xff0c;默认路由通常指本地网关的地址。 2.OSPF最主要的特征是使用分布式链路状态协议&#xff0c;而RIP使用的是距离向量协议。 3.OSPF使用链路状态公告LSA扩散路由信息 4.内部网关路由协议IGRP是一种动态距离矢量路由协议&a…...

大模型常见面试题

大模型常见面试题 大模型相关的面试问题通常涉及模型的原理、应用、优化以及面试者对于该领域的理解和经验。以下是一些常见的 大模型面试问题以及建议的回答方式&#xff1a; 请简述什么是大模型&#xff0c;以及它与传统模型的主要区别是什么&#xff1f; 回答&#xff1a…...

python高级编程一(生成器与高级编程)

@TOC 生成器 生成器使用 通过列表⽣成式,我们可以直接创建⼀个列表。但是,受到内存限制,列表容量肯定是有限的。⽽且,创建⼀个包含100万个元素的列表,不仅占⽤很⼤的存储空间,如果我们仅仅需要访问前⾯⼏个元素,那后⾯绝⼤多数元素占 ⽤的空间都⽩⽩浪费了。所以,如果…...

Linux线程属性与多线程开发:API详解与实战代码解析

Linux 线程的属性 线程池 多线程的创建 线程的属性 引入 我们设想一个场景&#xff0c;使用pthread_detach时&#xff0c;发现线程早就已经结束了&#xff0c;这时候pthread_detach还能正常发挥清理线程的 独有空间 的作用吗&#xff1f; 答案是可以的&#xff0c;但是这难…...

Inkscape安装教程

Inkscape 是一款开源的矢量图形编辑软件&#xff0c;功能强大且免费&#xff0c;适用于 Windows、macOS 和 Linux 系统。以下是在不同操作系统上安装 Inkscape 的详细教程&#xff1a; 一、Windows 系统安装 Inkscape 1. 下载安装包 打开浏览器&#xff0c;访问 Inkscape 官方…...

危化品安全员岗位注意事项有哪些?

危化品安全员肩负着保障危化品生产、储存、运输和使用等环节安全的重要职责&#xff0c;其岗位注意事项涉及多个方面&#xff0c;以下是一些主要内容&#xff1a; 法规标准与制度执行 必须熟悉并严格遵守国家和地方有关危化品安全管理的法律法规、标准规范&#xff0c;如《危险…...

1、从零搭建魔法工坊:React 19 新手村生存指南

一、开篇&#xff1a;新世界的入场券 "你好&#xff0c;年轻的魔法学徒&#xff01;欢迎来到React魔法世界。我是你的向导赫敏韦斯莱&#xff0c;今天我们将用React 19这根全新魔杖&#xff0c;搭建属于你的第一座魔法工坊。" ——以对话形式开场&#xff0c;消除技…...

链表代码实现(C++)

数据结构第三篇 一、几个注意点 1、同时持有头尾结点的引用 双链表一般同时持有头尾结点的引用 因为在工程应用中&#xff0c;通常在容器尾插入元素&#xff0c;双链表持有尾部节点的引用&#xff0c;就可以在O&#xff08;1&#xff09;时间复杂度的情况下在尾部添加元素。…...

【学习笔记】两个类之间的数据交互方式

在面向对象编程中&#xff0c;两个类之间的数据交互可以通过以下几种方式实现&#xff0c;具体选择取决于需求和设计模式&#xff1a; 1. 通过方法调用 一个类通过调用另一个类的公共方法来获取或传递数据。这是最常见的方式&#xff0c;符合封装原则。 class ClassA:def __…...

【Docker基础】深入解析 Docker 存储卷:管理、绑定与实战应用

文章目录 一、什么是存储卷二、为什么需要存储卷三、存储卷分类四、管理卷 Volume方式一&#xff1a;Volume 命令操作方式二&#xff1a;使用 -v 或 --mount 参数指定卷方式三&#xff1a;Dockerfile 匿名卷 五、操作案例Docker 命令创建管理卷Docker -v 创建管理卷Docker 卷生…...

Python生成exe

其中的 -w 参数是 PyInstaller 用于窗口模式&#xff08;Windowed mode&#xff09;&#xff0c;它会关闭命令行窗口的输出&#xff0c;这通常用于 图形界面程序&#xff08;GUI&#xff09;&#xff0c;比如使用 PyQt6, Tkinter, PySide6 等。 所以&#xff1a; 如果你在没有…...

SpringBoot原理

配置优先级 SpringBoot项目当中支持的三类配置文件&#xff1a; 在SpringBoot项目当中&#xff0c;我们要想配置一个属性&#xff0c;可以通过这三种方式当中的任意一种来配置都可以&#xff0c;那么如果项目中同时存在这三种配置文件&#xff0c;且都配置了同一个属性&#x…...

Google 官方提示工程 (Prompt Engineering)白皮书 总结

《大语言模型的提示工程&#xff1a;从基础到最佳实践》 总结 本文围绕大语言模型的提示工程展开&#xff0c;介绍其是设计高质量提示引导 LLM 产生准确输出的过程。探讨了 LLM 输出配置如输出长度、温度、top-K 和 top-P 等设置及其相互影响&#xff0c;阐述了零样本、少样本…...

Python——numpy测试题目

题目&#xff1a; 生成一个2行3列随机整数二维数组a使用Numpy方法对&#xff08;1&#xff09;中数组a进行整体求积使用Numpy方法对&#xff08;1&#xff09;中数组a进行求每列最大值索引定义一个NumPy一维数组 b&#xff0c;元素为 1 到 10 的整数获取&#xff08;4&#x…...

【SLAM】将realsense-viewer录制的rosbag视频导出成图片序列(RealSense D435)

本文介绍了如何将realsense-viewer录制的rosbag格式的视频导出成图片序列&#xff0c;方便合并成mp4视频或插入到论文中。 本文首发于❄慕雪的寒舍 说明 Intel提供的realsense-viewer软件录制的视频都是rosbag格式的&#xff0c;为了编写论文&#xff0c;需要从录制的视频中截…...

Unity6国际版下载

Unity6国际版下载下载地址 Hub下载地址&#xff1a;https://www.nounitycn.top/unityhub 先下载unity6启动器&#xff08;下载速度很快&#xff09;&#xff0c;在去下载unity6000版本&#xff08;下载速度慢&#xff09; 下载速度很慢的话&#xff0c;有条件可以找梯子科学上网…...

2025认证杯挑战赛B题【 谣言在社交网络上的传播 】原创论文讲解(含完整python代码)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了认证杯数学中国数学建模网络挑战赛第一阶段B题目谣言在社交网络上的传播完整的成品论文。 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 一、问题重述 二&#xff0e; 问题分析 2.1问题一 2.…...

后台进程管理之pstree 和 job

1. pstree 命令 功能 以树状结构显示进程间的父子关系&#xff0c;直观展示进程的层次结构。 常用选项 选项说明-p显示进程 PID-a显示完整命令行&#xff08;包括参数&#xff09;-u显示进程所属用户-n按 PID 排序&#xff08;默认按进程名&#xff09;-h高亮当前进程及其祖…...

波束形成(BF)从算法仿真到工程源码实现-第三节-延迟求和波束形成(DSB)

一、概述 本节我们讨论延迟求和波束形成算法&#xff0c;包括原理分析及代码实现。 更多资料和代码可以进入 https://t.zsxq.com/qgmoN &#xff0c;同时欢迎大家提出宝贵的建议&#xff0c;以共同探讨学习。 二、原理分析 2.1 原理&#xff1a; 首先对不同麦克风信号之间的相…...

deepseek使用记录——拉美文学的且战且败和且败且战

一 拉美文学&#xff0c;且战且败&#xff0c;且败且战&#xff0c;有哪些比较深刻的文学作品&#xff0c;对当下的年轻人有何启示。 拉丁美洲文学以其对历史、政治、社会现实的深刻反思和独特的魔幻现实主义风格闻名于世。这些作品既记录了拉美大陆在殖民、独裁、全球化浪潮中…...

LeetCode 解题思路 37(Hot 100)

解题思路&#xff1a; 初始化&#xff1a; 初始化最大举行 max 和栈 stack。左右补零&#xff1a; 考虑柱子递增的边界情况&#xff0c; 初始化填充柱状图 newHeights。遍历处理&#xff1a; 对于每一根遍历到的柱子 newHeights[i]&#xff0c;若柱子高度小于栈口索引&#xf…...

lvs+keepalived+dns高可用

1.配置dns相关服务 1.1修改ip地址主机名 dns-master: hostnamectl hostname lvs-master nmcli c modify ens160 ipv4.method manual ipv4.addresses 10.10.10.107/24 ipv4.gateway 10.10.10.2 ipv4.dns 223.5.5.5 connection.autoconnect yes nmcli c up ens160dns-salve: h…...

计算齿轮故障频率|平行轴|行星轮齿轮

一、平行轴齿轮故障频率 关键参数定义 Z&#xff1a;齿轮齿数 fs&#xff1a;轴旋转频率&#xff08;Hz&#xff09; N&#xff1a;啮合齿轮齿数&#xff08;配对齿轮&#xff09; 特征频率公式 软件页面截图 二、行星齿轮故障频率 系统组成参数 太阳轮齿数 齿圈齿数 …...

【技术派部署篇】云服务器部署技术派

1 环境搭建 1.1 JDK安装 # ubuntu sudo apt update # 更新apt apt install openjdk-8-jdk # 安装JDK安装完毕之后&#xff0c;执行 java -version 命令进行验证&#xff1a; 1.2 Maven安装 cd ~ mkdir soft cd soft wget https://dlcdn.apache.org/maven/maven-3/3.8.8/bina…...