钜形不锈钢水箱 发表于 2025-4-17 23:50:20

在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)自界说规则识别标题:在代码里添加自界说规则,按照常见标题序号格式识别标题,然后设置对应的标题级别。
(2)deepseek 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宏编辑器】。
https://i-blog.csdnimg.cn/direct/72c942bbad70423c91275e7131886ce7.png
2.在Project(Normal.dotm)中新建模块

Project(Normal.dotm)可以控制全局,即全部文档都可以用。
https://i-blog.csdnimg.cn/direct/e5ed0301f7d54e5c9b376d910f76b3cb.png
在模块中添加以下代码(参加当前的模块名是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 = false;

        hasBracketNumbers = false;

        hasPlainNumbers = 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 = true;

                } else if (bracketRegex.test(text)) {

                    if (j === 1) hasBracketOne = true;

                    hasBracketNumbers = true;

                } else if (plainRegex.test(text)) {

                    if (j === 1) hasNumberOne = true;

                    hasPlainNumbers = true;

                }

            }

        }

    }



    // 统计存在的标题序号类型数量

    var titleTypeCount = 0;

    for (var i = 1; i <= 10; i++) {

        if (hasChineseNumbers) titleTypeCount++;

        if (hasBracketNumbers) titleTypeCount++;

        if (hasPlainNumbers) 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 {

                // 只有一种标题序号类型,全部设为标题1

                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`);

                    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;

} 界面效果如下:
https://i-blog.csdnimg.cn/direct/d6b4e9e71cb64e81a43daf2882671ee5.png
接下来即可点击界面上的“运行”按钮,直至提示完成,即可看文档被设置后的效果!
https://i-blog.csdnimg.cn/direct/6ae87d2faf094180bba5bdcd4795a339.png

三、结合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.message.content;



            // 解析 API 结果

            var lines = result.split('\n');

            for (var i = 0; i < lines.length; i++) {

                var line = lines;

                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.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.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.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选项卡。我们继承在这个选项卡下新增。
https://i-blog.csdnimg.cn/direct/17a8c7cb1e604df9952cffb662403090.png
(2)新建组
前文我们也已经添加了“AI优化”组件。接下来我们再添加一个“创建标题与目次”的组。如下图:
https://i-blog.csdnimg.cn/direct/410146a8f9fe4548894c7aefd59a8643.png
(3)为“创建标题与目次”组添加组件
选中“创建标题与目次”组,在左侧下来选择“宏”,选择identifyAndSetHeadingsAndCreateTOC,之后点击“添加”,将组件添加到“创建标题与目次”下。
https://i-blog.csdnimg.cn/direct/a221b7e04b1d4c21bfbcf83db3b5fe89.png
(4)组件改名
对Project.Module2.identifyAndSetHeadingsAndCreateTOC改名:创建标题与目次。可得如下效果:
https://i-blog.csdnimg.cn/direct/8230b9bd1cde4a35b1c6f8c342b587a3.png
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 = false;
      hasBracketNumbers = false;
      hasPlainNumbers = 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 = true;
                } else if (bracketRegex.test(text)) {
                  if (j === 1) hasBracketOne = true;
                  hasBracketNumbers = true;
                } else if (plainRegex.test(text)) {
                  if (j === 1) hasNumberOne = true;
                  hasPlainNumbers = true;
                }
            }
      }
    }

    // 统计存在的标题序号类型数量
    var titleTypeCount = 0;
    for (var i = 1; i <= 10; i++) {
      if (hasChineseNumbers) titleTypeCount++;
      if (hasBracketNumbers) titleTypeCount++;
      if (hasPlainNumbers) 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 {
                // 只有一种标题序号类型,全部设为标题1
                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`);
                  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.message.content;

            // 解析 API 结果
            var lines = result.split('\n');
            for (var i = 0; i < lines.length; i++) {
                var line = lines;
                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.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.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.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;
}

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API主动识别标题级别和目次