冰蓝科技
|
028-81705109
|
|
微信扫一扫
|

Spire.Cloud 纯前端文档控件

E-iceblue 秋季限时特惠来袭!采购或续费指定 Spire.Office for .NET 产品, 免费赠送 Spire.Agent.Office 正式授权。 本次活动时间为 8 月 24 日至 9 月 24 日, 企业与开发者无需额外付费,即可为文档自动化工作流搭载 AI 能力。

Spire.Agent.Office, 是一套 AI 智能代理套件,专为强化 Spire.Office 成熟的文档处理 SDK 技术栈而设计。开发者只需通过自然语言指令,即可完成 Word、Excel、PowerPoint 和 PDF 文件的创建、润色、审阅、转换、格式调整及内容提取等操作。 相较于传统文档 SDK 需要为每个环节编写大量代码,Spire.Agent.Office 依托自适应的 AI 逻辑,可显著降低开发成本,加速企业级文档自动化方案的落地。

活动内容:

  • 新购订单:: 获赠 2 个月 完整版 Spire.Agent 许可证。
  • 续费订单:: 获赠 1 个月 完整版 Spire.Agent 许可证。

适用产品及免费 AI Agent 版本

每笔符合条件的购买或续订,将自动获赠与产品等级对应的 Spire.Agent 许可证。下表为赠送许可证对应规格及价值:

购买产品 获赠 AI Agent 版本及 2 个月赠品价值(续订为 1 个月)
Spire.Office for .NET Spire.Agent.Office 个人版 → 探索版(11,998 元)
微企版 → 微企版(47,998 元)
SITE OEM → 企业版(139,998 元)
.NET 平台独立组件
(Doc / XLS / Presentation / PDF)
Spire.Agent.Doc
Spire.Agent.XLS
Spire.Agent.Presentation
Spire.Agent.PDF
个人版 → 探索版(3,998 元)
微企版 → 微企版(17,998 元)
SITE OEM → 企业版(47,998 元)

注:所有赠品均为完整授权版本,非试用版,且仅通过 E-iceblue 官方渠道提供。


如何领取免费 AI 许可证

参与流程简单,全程自动处理:

1. 在活动期间(8月24日 – 9月24日),通过 E iceblue 官方渠道采购或续费任意符合活动条件的 Spire 产品。

2. 查收邮件:订单完成后,Spire.Agent 许可证及激活指引将自动发送至您的邮箱。

3. 若未收到邮件,可随时联系 E iceblue 销售团队进行人工补发。


重要活动条款

  • 本优惠仅适用于 E iceblue 官方渠道下单,第三方经销商采购订单不参与本次活动。
  • Spire.Agent 免费许可证的有效期自激活之日起计算。
  • 赠送许可证不可兑换现金,也不得折算为其他补偿。
  • E-iceblue 保留随时修改、暂停或终止本优惠活动的权利。

AI 赋能,升级文档处理流程

把握限时专属优惠,采购 Spire 产品即可免费获取 Spire.Agent 授权,全面提升文档处理效能。

  • 浏览产品: https://www.e-iceblue.cn/Downloads.html
  • 活动详情: https://www.e-iceblue.cn/Campaign.html
  • 销售咨询:该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。(电话:86-028-81705109)

Spire.XLS for Python 16.8.2 现已正式发布。该版本支持宏(VBA)功能,包括添加、获取和删除 VBA 工程,以及转换 Excel 到 PDF 时配置 PDF 加密。同时,在组合使用多个 CopyRangeOptions 枚举值时报错的问题也得以成功被修复。更多详情如下。

新功能:

问题修复:


获取 Spire.XLS for Python 16.8.2 请点击:

https://www.e-iceblue.cn/Downloads/Spire-XLS-Python.html

Spire.Agent.Office 11.8.3 现已发布。该版本新增支持 .NET Standard 2.1,实现跨平台无缝使用;引入 ApplyLicense() 方法,支持通过 SpireToken Key 便捷激活并使用 SDK 全部功能。同时新增对自定义 AI 模型的支持(兼容 Anthropic 协议模型),并针对稳定性、响应速度及用户体验进行了多项优化。详情如下。

新功能:

优化:

问题修复:


了解更多 Spire.Agent.Office 11.8.3 请点击:

https://www.e-iceblue.cn/downloads/spire-agent-office.html

我们很高兴地宣布 Spire.Presentation for .NET 11.8.4 版本正式发布。本次更新重点修复了 PPTX 转 PDF 转换过程中存在的多项渲染与导出异常问题,实现更稳定、精准、视觉效果统一的文档转换效果。详细更新内容如下。

问题修复:


获取 Spire.Presentation 11.8.4 请点击:

https://www.e-iceblue.cn/Downloads/Spire-Presentation-NET.html

Spire.XLS 16.8.2 现已正式发布。该版本新增了对 TEXTSPLIT 公式的支持,并修复了 OFD 流保存、混合数据类型列导出以及公式计算等方面的问题。详情如下。

新功能:

问题修复:


获取 Spire.XLS 16.8.2 请点击:

https://www.e-iceblue.cn/Downloads/Spire-XLS-NET.html

条件格式是 Excel 中直观展示数据的重要手段,它根据单元格的数值或日期自动应用颜色、条形等可视化效果,让报表中的高低值、关键日期一目了然。Spire.XLS for JavaScript 基于 WebAssembly 在浏览器端直接为单元格区域添加条件格式,通过虚拟文件系统(VFS)管理输入输出文件,无需后端服务支持。

本文介绍三个核心功能点:

有关安装和项目配置,请参考 React 项目中集成 Spire.XLS for JavaScript。以下示例默认已安装 Spire.XLS 并完成 WebAssembly 模块初始化。


在单元格区域应用数据条

数据条通过单元格中填充的横向条状长度直观反映数值的相对大小,数值越大,条形越长。Spire.XLS for JavaScript 通过 ConditionalFormats.Add 方法创建条件格式集合,再通过 AddCondition 添加数据条条件,并使用 DataBar.BarColor 自定义条形颜色。

function App() {
  const sheetToSVG = async () => {
    // 获取 Spire.XLS WASM 模块
    const xlsModule = window.wasmModule?.spirexls;

    // 检查模块是否就绪
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // 将字体文件载入 VFS
    await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);

    // 创建工作簿
    const workbook = new xlsModule.Workbook();

    // 获取第一个工作表
    const sheet = workbook.Worksheets.get(0);

    // 向 A1:C4 单元格区域写入数据
    sheet.Range.get("A1").NumberValue = 582;
    sheet.Range.get("A2").NumberValue = 234;
    sheet.Range.get("A3").NumberValue = 314;
    sheet.Range.get("A4").NumberValue = 50;
    sheet.Range.get("B1").NumberValue = 150;
    sheet.Range.get("B2").NumberValue = 894;
    sheet.Range.get("B3").NumberValue = 560;
    sheet.Range.get("B4").NumberValue = 900;
    sheet.Range.get("C1").NumberValue = 134;
    sheet.Range.get("C2").NumberValue = 700;
    sheet.Range.get("C3").NumberValue = 920;
    sheet.Range.get("C4").NumberValue = 450;
    sheet.AllocatedRange.RowHeight = 15;
    sheet.AllocatedRange.ColumnWidth = 17;

    // 添加条件格式并应用到数据区域
    const xcfs = sheet.ConditionalFormats.Add();
    xcfs.AddRange(sheet.AllocatedRange);

    // 添加数据条条件格式并设置条形颜色
    const format = xcfs.AddCondition();
    format.FormatType = xlsModule.ConditionalFormatType.DataBar;
    format.DataBar.BarColor = xlsModule.Color.get_CadetBlue();

    const outputFileName = 'ApplyDataBarsToCellRange_out.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });

    // 释放 workbook 对象以释放资源
    workbook.Dispose();

    // 从 VFS 读取转换后的文件,触发下载
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
    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>Apply Data Bars to Cell Range</h1>
      <button onClick={sheetToSVG}>
        Start
      </button>
    </div>
  );
}

export default App;

在单元格区域应用数据条效果 在单元格区域应用数据条效果


根据日期设置条件格式

在项目管理、销售报表等场景中,我们常常需要高亮显示最近一段时间内的日期,例如最近 7 天内的记录。Spire.XLS for JavaScript 通过 AddTimePeriodCondition 方法添加基于时间段的日期条件格式,并以 TimePeriodType 枚举指定时间范围。

function App() {
  const sheetToSVG = async () => {
    // 获取 Spire.XLS WASM 模块
    const xlsModule = window.wasmModule?.spirexls;

    // 检查模块是否就绪
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // 将字体和 Excel 文件载入 VFS
    await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ConditionallyFormatDate.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // 加载工作簿
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // 获取第一个工作表
    const sheet = workbook.Worksheets.get(0);

    // 添加条件格式并应用到数据区域
    const xcfs = sheet.ConditionalFormats.Add();
    xcfs.AddRange(sheet.AllocatedRange);

    // 高亮最近 7 天内日期的单元格
    const conditionalFormat = xcfs.AddTimePeriodCondition(xlsModule.TimePeriodType.Last7Days);
    conditionalFormat.BackColor = xlsModule.Color.get_Orange();

    const outputFileName = 'ConditionallyFormatDate_out.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });

    // 释放 workbook 对象以释放资源
    workbook.Dispose();

    // 从 VFS 读取转换后的文件,触发下载
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
    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>Conditionally Format Date</h1>
      <button onClick={sheetToSVG}>
        Start
      </button>
    </div>
  );
}

export default App;

设置日期条件格式前 设置日期条件格式前 设置日期条件格式后 设置日期条件格式后


通过公式创建条件格式

当内置条件格式无法满足需求时,可以使用公式自定义判断规则。Spire.XLS for JavaScript 支持将 ConditionalFormatType 设置为 Formula,并通过 FirstFormula 指定判断公式,满足条件的单元格将应用所设置的背景色。

function App() {
  const sheetToSVG = async () => {
    // 获取 Spire.XLS WASM 模块
    const xlsModule = window.wasmModule?.spirexls;

    // 检查模块是否就绪
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // 将字体和 Excel 文件载入 VFS
    await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ConditionallyFormatDate.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // 加载工作簿
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // 获取第一个工作表及其第一列
    const sheet = workbook.Worksheets.get(0);
    const range = sheet.Columns.get(0);

    // 添加条件格式并应用到第一列
    const xcfs = sheet.ConditionalFormats.Add();
    xcfs.AddRange(range);

    // 设置条件格式公式:A 列单元格值小于同行 B 列值时应用格式
    const conditional = xcfs.AddCondition();
    conditional.FormatType = xlsModule.ConditionalFormatType.Formula;
    conditional.FirstFormula = "=($A1<$B1)";
    conditional.BackKnownColor = xlsModule.ExcelColors.Yellow;

    const outputFileName = 'CreateFormulaConditionalFormat_out.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });

    // 释放 workbook 对象以释放资源
    workbook.Dispose();

    // 从 VFS 读取转换后的文件,触发下载
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
    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>Create Formula Conditional Format</h1>
      <button onClick={sheetToSVG}>
        Start
      </button>
    </div>
  );
}

export default App;

通过公式设置条件格式 通过公式设置条件格式


常见问题

保存的文件在旧版 Excel 中无法显示条件格式

原因:数据条、时间段、公式等条件格式属于 Excel 2007+(XLSX)格式能力,若以旧版本格式保存可能导致条件格式丢失或无法显示。

解决:保存时显式指定文件版本为 Excel 2010,例如:

workbook.SaveToFile({
  fileName: 'output.xlsx',
  version: xlsModule.ExcelVersion.Version2010
});

日期条件格式没有生效

原因:目标单元格中的日期数据实际为文本或普通数字,并非真正的日期值,导致基于时间段的判断无法匹配。

解决:确保工作表中的日期以日期格式存储,例如在生成测试数据时直接写入日期类型值,而非字符串。

公式条件格式引用范围有误

原因:FirstFormula 中公式的相对引用与单元格区域不对应,导致判断结果与预期不符。

解决:确认公式中的行列引用与所选区域一致,例如对整列 A 应用 =($A1<$B1) 时,公式以所选区域首个单元格作为参考起点。


获取免费许可证

如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。

在汇报演示前,PPT 的宽高比往往需要统一,有的会议屏是 4:3 标准比例,有的投影仪是 16:9 宽屏。这就需要版式双向转换,而且转换过程中需要自动调整文字大小与图片位置,确保内容正常显示、无文字溢出或图片错位。本文介绍如何使用 Spire.Agent.Office PowerPoint AI 能力,将 PPT 演示文稿在 4:3 标准比例与 16:9 宽屏比例之间批量转换。

对比传统SDK API处理

传统 Spire.Office for .NET API Spire.Agent.Office 处理
驱动方式 硬编码的API调用 1条自然语言指令
需求变更 需求变更需要修改代码并重新部署 需求变化时只需修改指令文本,无需重新编译代码

有关产品安装和 SpireToken 配置,请参考 在 .NET 项目中集成 Spire.Agent.Office。以下示例默认已安装 Spire.Agent.Office 并完成 SpireToken 配置。


4:3 标准转 16:9 宽屏

所有幻灯片的内容结构、主题与配色保持不变,文字大小与图片位置自动适配宽屏画布,多页 PPT 一次转换完成。

using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Presentation;

// PowerPoint AI处理配置
string inputPath = @"ratio_43.pptx";  
string savePath = @"output.pptx";  
// SpireToken Key
string key = "**************************";  
string instruction = "读取输入PPT演示文稿,将其从 4:3 标准比例批量转换为 16:9 宽屏比例";
AIResult result = ExecuteDemoPpt(instruction, inputPath, savePath, key);

// 执行PowerPoint文档AI处理
static AIResult ExecuteDemoPpt(string instruction, string inputPath, string savePath, string key)
{
    // 创建AIOptions选项配置对象
    AIOptions options = new AIOptions();
    options.SpireToken = key;  

    // 使用Presentation对象处理PowerPoint文档
    using (Presentation ppt = new Presentation())
    {
        // 从文件加载PPT
        if (!string.IsNullOrEmpty(inputPath) && File.Exists(inputPath))
        {
            ppt.LoadFromFile(inputPath);
        }
        // 创建AI文档处理器
        AIDocumentProcessor processor = ppt.AI(options);

        // 执行AI指令
        return processor.ExecuteInstruction(ppt, instruction, savePath);
    }
}

原 4:3 标准 PPT 原 4:3 标准 PPT 转换后的 16:9 宽屏 PPT 转换后的 16:9 宽屏 PPT


16:9 宽屏转 4:3 标准

using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Presentation;

// PowerPoint AI处理配置
string inputPath = @"ratio_169.pptx";  
string savePath = @"output.pptx"; 
// SpireToken Key
string key = "**************************";  
string instruction ="读取输入PPT演示文稿,将其从 16:9 宽屏比例批量转换为 4:3 标准比例";

// 调用PowerPoint文档处理函数
AIResult result = ExecuteDemoPpt(instruction, inputPath, savePath, key);

// 执行PowerPoint文档AI处理(与上文相同的辅助函数)
static AIResult ExecuteDemoPpt(string instruction, string inputPath, string savePath, string key)
{
    AIOptions options = new AIOptions();
    options.SpireToken = key;

    using (Presentation ppt = new Presentation())
    {
        if (!string.IsNullOrEmpty(inputPath) && File.Exists(inputPath))
        {
            ppt.LoadFromFile(inputPath);
        }
        AIDocumentProcessor processor = ppt.AI(options);
        return processor.ExecuteInstruction(ppt, instruction, savePath);
    }
}

原 16:9 宽屏 PPT 原 16:9 宽屏 PPT 转换后的 4:3 标准 PPT 转换后的 4:3 标准 PPT


常见问题

结果文档页丢失

原因:如果原文档页数较多,AI处理分析相对会耗时些,AIOptions.TimeoutMs超时设置默认为5分钟,如果超出,AI分析会中断。

解决:设置AIOptions.TimeoutMs足够时间,例如:

 AIOptions options = new AIOptions();    
    options.TimeoutMs = 1000000;

获取SpireToken Key

  • 联系 该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。 或访问 https://www.e-iceblue.com/TemLicense.html 获得试用/商业 API 密钥

在代码中配置:

AIOptions options = new AIOptions();
options.SpireToken = key;

在教学工作中,备课是每一位老师耗时最多、也最考验功底的环节。拿到一本教材,需要逐章逐节通读内容、提炼核心知识点、梳理知识逻辑,再据此设计教学目标、确定教学重难点、编排导入—新授—巩固—小结的完整教学过程,最后整理成规范的教案。一套完整的教案往往需要数小时,且不同老师的解析深度与教案结构千差万别,质量很难保证统一。

对比传统SDK API处理

传统 Spire.Office for .NET API Spire.Agent.Office
驱动方式 编写代码逐段解析教材:加载文档→遍历段落→关键词提取→手工拼装教案,每步需代码控制 自然语言描述解析与生成目标,AI 自动理解教材并生成教案
代码量 需大量代码维护知识要点库、段落分类规则和教案模板逻辑 仅需配置代码 + 1 条自然语言指令
教案结构 教学目标、重难点、教学过程需分别硬编码一套生成逻辑 AI 按学科规范自动生成结构完整的教案
教材理解 只能按关键词机械匹配,无法理解知识点之间的关联与层级 AI 基于语义理解教材,可提炼章节主旨、考点与教学建议
维护性 不同科目、不同教材版本需单独开发维护 解析范围与教案风格可用自然语言随时调整

本文介绍如何使用 Spire.Agent.Office Word AI 能力对教材进行智能解析并自动生成教案,两者构成一条完整的备课链路:先用 AI 解析教材 PDF、梳理单元要点与重难点,再基于解析结果生成格式规范、内容完整的课时教案。

有关产品安装和 SpireToken 配置,请参考 在 .NET 项目中集成 Spire.Agent.Office。以下示例默认已安装 Spire.Agent.Office 并完成 SpireToken 配置。


教材智能解析

教材智能解析是整套备课流程的起点,适合在通读全书前快速建立对教材的整体认知。其核心思路是:将教材电子版 PDF 作为附件传入,让 AI 解析教材内容,按章节梳理教材的核心知识、重点难点、学习建议与章节间的联系,生成一份 Word 单元教材解析文档,老师据此完成单元教学规划,后续课时教案的生成也以它为背景。

using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Doc;

// 教材 PDF(可传入多个章节)
string[] attachments = new string[] {
    "E:\\输入\\教材-有理数.pdf",
    "E:\\输入\\教材-整式的加减.pdf",
    "E:\\输入\\教材-一元一次方程.pdf"
};
// 保存路径
string savePath = "E:\\输出\\教材解析.docx";
// SpireToken Key
string key = "**********************";
// 自然语言指令
string instruction =
    "请解析附件 PDF 中的教材内容,以备课老师的口吻帮我梳理一份适合日常备课的教材解析,\n" +
    "按章节说明每章的核心知识内容、教学重点与难点、建议课时,\n" +
    "每节尽量保留关键概念与典型例题的要点,并补充学生学习本章时常见的困难与易错点,\n" +
    "以及各章之间的前后联系。请严格依据 PDF 中的教材实际内容分析,不要编造,\n" +
    "按清晰的结构生成 Word 文档,方便我据此安排单元教学计划,后续教案也以这份解析为依据。";

// 调用Word文档处理函数
AIResult result = ExecuteDemoWord(instruction, savePath, key, attachments);

// 执行Word文档AI处理
static AIResult ExecuteDemoWord(string instruction, string savePath, string key, string[] attachments)
{
    // 创建AIOptions选项配置对象
    AIOptions options = new AIOptions();
    // 设置SpireToken Key
    options.SpireToken = key;

    // 使用Document对象处理Word文档
    using (Document doc = new Document())
    {
        // 创建AI文档处理器
        AIDocumentProcessor processor = doc.AI(options);

        // 执行AI指令
        return processor.ExecuteInstruction(doc, instruction, savePath, attachments);
    }
}

AI 生成的 Word 教材解析文档 教材解析文档

解析文档按章节展开,清楚说明每章的核心知识、重点难点与建议课时,还补充了学生常见的学习困难与易错点、章节间的联系。老师只需提供教材的电子版 PDF,即可完成全书梳理,快速圈定重点章节、合理分配课时;这份单元教材解析还可以直接作为下一步课时教案生成的背景材料。


Word教案自动化生成

单节课的精细化备课,可以在第一节教材解析的基础上继续推进。其核心思路是:把第一节生成的单元教材解析直接作为输入,让 AI 依据其中该节的教材分析生成结构完整、可直接使用的课时教案,包含学情分析、教学目标、教学重难点、教学准备、教学过程、板书设计与课后分层作业。

using Spire.Agent.Office.AI;
using Spire.Agent.Office.Extensions;
using Spire.Doc;

// 第一节生成的单元教材解析(已包含该节教材内容分析)
string inputPath = "E:\\输入\\教材解析.docx";
// 保存路径
string savePath = "E:\\输出\\一元一次方程-教案.docx";
// SpireToken Key
string key = "**********************";
// 自然语言指令
string instruction =
    "请结合单元教材解析文档中《一元一次方程》一节的内容," +
    "帮我写一份完整的课时教案 Word 文档,建议包含:" +
    "学情分析、教学目标(知识与技能、过程与方法、情感态度与价值观)、教学重难点、教学准备、" +
    "教学过程(导入、新授、巩固练习、课堂小结)、板书设计和课后分层作业。" +
    "教学目标与重难点要紧密结合教材内容,教学过程要具体到每个环节老师怎么引导、学生怎么学," +
    "课后作业按基础题与提高题分层设计。请按规范的教案格式排版,统一标题层级与字体,最终保存输出DOCX格式";

// 调用Word文档处理函数
AIResult result = ExecuteDemoWord(instruction, inputPath, savePath, key, null);

// 执行Word文档AI处理
static AIResult ExecuteDemoWord(string instruction, string inputPath, string savePath, string key, string[] attachmentPaths)
{
    // 创建AIOptions选项配置对象
    AIOptions options = new AIOptions();
    // 设置SpireToken Key
    options.SpireToken = key;

    // 使用Document对象处理Word文档
    using (Document doc = new Document())
    {
        // 加载单元教材解析文档作为教案生成上下文
        if (!string.IsNullOrEmpty(inputPath) && File.Exists(inputPath))
        {
            doc.LoadFromFile(inputPath);
        }
        // 创建AI文档处理器
        AIDocumentProcessor processor = doc.AI(options);

        // 执行AI指令
        return processor.ExecuteInstruction(doc, instruction, savePath, attachmentPaths);
    }
}

AI 生成的 Word 教案文档 自动生成的Word教案

生成的教案结构完整、内容贴合教材,学情分析与课后分层作业等环节也一并覆盖。直接基于单元教材解析,老师即可拿到课时初稿,在此基础上调整润色,省去了从零撰写的时间。对于同一单元的多节课,也可复用同一份单元教材解析逐节生成教案再统一校对,让备课从"逐字撰写"变为"局部修改"。


常见问题

教学目标不贴合教材内容

原因:AI 对教学目标的生成依赖对教材主旨的理解,若教材内容较多或指令描述过泛,目标可能与实际教学内容脱节。

解决:在指令中限定解析范围(如指定章节名),并明确要求目标从三个维度展开,同时绑定"需结合教材实际内容撰写"。

教案结构不规范、缺少环节

原因:未在指令中说明教案应包含的章节结构,AI 默认生成的结构可能与学校模板不一致。

解决:在指令中按顺序列出教案必须包含的环节(如导入、新授、巩固、小结),AI 将严格按此结构输出。

解析报告遗漏考点或知识点

原因:教材章节过多,或同一知识点分散在多个章节,导致解析报告不完整。

解决:将完整教材或相关章节的 PDF 作为附件一并传入,并在指令中说明关注的知识类型(如"关注常考题型与例题")。

生成教案排版不统一

原因:未在指令中说明教案的排版要求,AI 输出的标题层级、字体与段落样式可能不一致。

解决:在指令中补充"按规范的教案格式排版,统一标题层级与字体"等描述。


获取SpireToken Key

  • 联系 该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。 或访问 https://www.e-iceblue.com/TemLicense.html 获得试用/商业 API 密钥

在代码中配置:

AIOptions options = new AIOptions();
options.SpireToken = key;

在实际工作中,我们经常需要通过隐藏部分工作表来简化界面展示或保护敏感数据,也可以在需要时将其取消隐藏。此外,当把工作簿转换为 HTML 时,可能还需要控制隐藏的工作表是否出现在转换结果中。Spire.XLS for JavaScript 基于 WebAssembly 在浏览器端直接完成这些操作,通过虚拟文件系统(VFS)管理输入输出文件,无需后端服务支持。

本文介绍三个核心功能点:

有关安装和项目配置,请参考 React 项目中集成 Spire.XLS for JavaScript。以下示例默认已安装 Spire.XLS 并完成 WebAssembly 模块初始化。


隐藏工作表

隐藏工作表常用于简化工作簿的显示或保护内部数据。Spire.XLS for JavaScript 通过设置工作表对象的 Visibility 属性为 WorksheetVisibility.Hidden 即可隐藏指定的工作表。

function App() {
  const hideSheet = async () => {
    // 获取 Spire.XLS WASM 模块
    const xlsModule = window.wasmModule?.spirexls;

    // 检查模块是否就绪
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // 将字体和 Excel 文件载入 VFS
    await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'HideOrShowWorksheet.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // 加载工作簿
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // 获取名为 "Sheet1" 的工作表并隐藏
    let sheet1 = workbook.Worksheets.get("Sheet1");
    sheet1.Visibility = xlsModule.WorksheetVisibility.Hidden;

    // 保存工作簿
    const outputFileName = "HideWorksheet_output.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // 释放资源
    workbook.Dispose();

    // 从 VFS 读取转换后的文件,触发下载
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
    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>Hide Worksheet</h1>
      <button onClick={hideSheet}>
        Start
      </button>
    </div>
  );
}

export default App;

原文档(已隐藏Sheet2) 原文档 设置隐藏Sheet1 设置隐藏Sheet1


显示隐藏的工作表

当需要再次查看或编辑被隐藏的工作表时,可以通过将 Visibility 属性设置为 WorksheetVisibility.Visible 来重新显示工作表。

function App() {
  const showSheet = async () => {
    // 获取 Spire.XLS WASM 模块
    const xlsModule = window.wasmModule?.spirexls;

    // 检查模块是否就绪
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // 将字体和 Excel 文件载入 VFS
    await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'HideOrShowWorksheet.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // 加载工作簿
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // 获取第二个工作表并设置为可见
    let sheet2 = workbook.Worksheets.get(1);
    sheet2.Visibility = xlsModule.WorksheetVisibility.Visible;

    // 保存工作簿
    const outputFileName = "ShowWorksheet_output.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // 释放资源
    workbook.Dispose();

    // 从 VFS 读取转换后的文件,触发下载
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
    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>Show Worksheet</h1>
      <button onClick={showSheet}>
        Start
      </button>
    </div>
  );
}

export default App;

设置取消Sheet2的隐藏 设置取消Sheet2的隐藏


转换为HTML时控制是否含隐藏工作表

转换为 HTML时,可通过 SaveToHtml 方法的 skipHideSheet 参数控制转换结果是否包含隐藏的工作表。设置为 false 时,转换生成的 HTML 包含隐藏工作表;设置为 true 时,隐藏工作表会被跳过,HTML 中只保留可见的工作表。

function App() {
  const saveToHtml = async () => {
    // 获取 Spire.XLS WASM 模块
    const xlsModule = window.wasmModule?.spirexls;

    // 检查模块是否就绪
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // 将字体和 Excel 文件载入 VFS
    await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'HideOrShowWorksheet.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // 加载工作簿
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // 隐藏名为 "Sheet1" 的工作表
    let sheet1 = workbook.Worksheets.get("Sheet1");
    sheet1.Visibility = xlsModule.WorksheetVisibility.Hidden;

    // 设置输出 HTML 文件名称
    const result = "result.html";

    // false --- 转换时包含隐藏工作表
    // true --- 转换时跳过(排除)隐藏工作表
    workbook.SaveToHtml({
      fileName: result,
      skipHideSheet: false
    });

    // 释放资源
    workbook.Dispose();

    // 从 VFS 读取转换后的文件,触发下载
    const fileArray = window.dotnetRuntime.Module.FS.readFile(result);
    const blob = new Blob([fileArray], { type: 'text/html' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = result;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Convert Workbook to HTML</h1>
      <button onClick={saveToHtml}>
        Start
      </button>
    </div>
  );
}

export default App;

转换后 转换后


常见问题

HTML 转换结果多出其它工作表

原因:原Excel 文档有多个隐藏工作表,SaveToHtml 的 skipHideSheet 参数设置false时,所有隐藏工作表都会出现在转换结果中。

解决:可以使用下面代码遍历查看Excel所有sheet 的隐藏属性。

    const sheetCount = workbook.Worksheets.Count;
    for (let i = 0; i < sheetCount; i++) {
        let sheet = workbook.Worksheets.get(i);
        const visibility = sheet.Visibility;
    }

获取免费许可证

如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。

合并 PDF 文件是文档管理应用程序中的常见需求。例如,React 应用程序可能需要在文件归档或共享之前,将发票、报告、合同或扫描页面合并为一个 PDF。当源文档无需上传到服务器时,在浏览器中执行操作还可以简化工作流程。

在本教程中,您将学习如何使用 Spire.PDF for JavaScript 在 React 应用程序中合并 PDF 文档。第一个示例一次性合并多个完整的 PDF 文件。第二个示例提供了更精确的控制,从不同的 PDF 中选取指定页面并添加到新文档中。

本页内容:

在 React 项目中安装 Spire.PDF for JavaScript

在 React 项目的根目录中打开终端,安装 spire.office 包:

npm i spire.office

安装完成后,将安装包中的以下运行时文件和文件夹复制到 React 项目的 public 文件夹中:

public/
├── _framework/
├── spire.pdf.js
├── Spire.Pdf.Wasm.zip
├── spire.common.js
└── Spire.Common.Wasm.zip

JavaScript 加载器、WebAssembly 资源和配套框架文件在应用程序运行时必须作为静态资源保持可访问。有关详细的设置说明和确切的集成过程,请参阅 如何在 React 项目中集成 Spire.PDF for JavaScript。

对于本文中的示例,还需将输入的 PDF 文件放置在 public 文件夹中,以便应用程序可以通过 fetch() 获取它们:

public/
├── input_1.pdf
├── input_2.pdf
├── input_3.pdf
└── ...

在 React 中合并多个 PDF 文档

如果源文件的每一页都应出现在结果中,最直接的方法是使用 PdfMerger.Merge() 方法。它接受一个输入文件路径数组,按照数组中的顺序合并文件,并将结果写入 WebAssembly 虚拟文件系统。

以下 React 组件将 input_1.pdf、input_2.pdf 和 input_3.pdf 合并为一个名为 MergedPdf.pdf 的文档:

import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);
  const [isGenerating, setIsGenerating] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');

  useEffect(() => {
    (async () => {
      try {
        const publicUrl = process.env.PUBLIC_URL || '';
        const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.pdf.js`);
        const rawModule = spireModule.default || spireModule;
        window.wasmModule = typeof rawModule === 'function'
          ? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
          : rawModule;
        setWasmModule(window.wasmModule);
      } catch (error) {
        console.error('Failed to load spire.pdf.js:', error);
      }
    })();
  }, []);

  const loadPdfToVfs = async (fileName) => {
    const publicUrl = process.env.PUBLIC_URL || '';
    const response = await fetch(`${publicUrl}/${fileName}`);

    if (!response.ok) {
      throw new Error(`Failed to load ${fileName}: ${response.status}${response.statusText}`);
    }

    const fileBytes = new Uint8Array(await response.arrayBuffer());
    const pdfHeader = String.fromCharCode(...fileBytes.slice(0, 4));

    if (pdfHeader !== '%PDF') {
      throw new Error(`${fileName} was loaded, but it is not a valid PDF file.`);
    }

    window.dotnetRuntime.Module.FS.writeFile(fileName, fileBytes, { flags: 'w+' });
    return fileName;
  };

  const MergePdfs = async () => {
    const wasmModule = window.wasmModule?.spirepdf;
    if (!wasmModule || isGenerating) {
      return;
    }

    setIsGenerating(true);
    setErrorMessage('');

    try {
      const inputFiles = await Promise.all([
        loadPdfToVfs('input_1.pdf'),
        loadPdfToVfs('input_2.pdf'),
        loadPdfToVfs('input_3.pdf'),
      ]);

      const outputFileName = 'MergedPdf.pdf';
      const mergeOp = new wasmModule.MergerOptions();
      wasmModule.PdfMerger.Merge({
        inputFiles,
        outputFile: outputFileName,
        pdfMergeOptions: mergeOp
      });

      const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
      const modifiedFile = new Blob([modifiedFileArray], { type: 'application/pdf' });
      const url = URL.createObjectURL(modifiedFile);
      const a = document.createElement('a');

      a.href = url;
      a.download = outputFileName;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    } catch (error) {
      console.error('Failed to merge PDFs:', error);
      setErrorMessage(error.message || 'Failed to merge PDFs.');
    } finally {
      setIsGenerating(false);
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Merge PDF Documents in React</h1>
      <button onClick={MergePdfs} disabled={!wasmModule || isGenerating}>
        {isGenerating ? 'Generating...' : 'Generate'}
      </button>
      {errorMessage && <p style={{ color: 'crimson' }}>{errorMessage}</p>}
    </div>
  );
}

export default App;

输出:

Merge Multiple PDF Documents

代码工作原理

该组件首先在 useEffect() 中加载 spire.pdf.js。由于模块是异步初始化的,因此 Generate 按钮在运行时就绪之前保持禁用状态。

loadPdfToVfs() 函数随后为每个源文档执行三项任务:

  1. 使用 fetch() 从 public 目录获取 PDF。
  2. 检查前四个字节是否为 %PDF 签名,以帮助捕获缺失文件或非 PDF 响应。
  3. 将文件字节写入 WebAssembly 虚拟文件系统,以便 Spire.PDF 可以访问它们。

所有三个文件加载完成后,PdfMerger.Merge() 按照 inputFiles 指定的顺序合并它们。输出从虚拟文件系统中读取,转换为 PDF Blob,并通过临时对象 URL 下载。

要更改合并顺序,只需重新排列数组中的条目即可。例如,以下顺序会将 input_3.pdf 放在最前面:

const inputFiles = await Promise.all([
  loadPdfToVfs('input_3.pdf'),
  loadPdfToVfs('input_1.pdf'),
  loadPdfToVfs('input_2.pdf'),
]);

在 React 中合并来自不同 PDF 文档的选定页面

并非总是需要合并完整的文档。您可能需要从一个文件中提取封面页,从另一个文件中提取页面范围来创建新的 PDF。在这种情况下,将源文件加载为 PdfDocument 对象,并使用 InsertPage() 和 InsertPageRange() 来构建输出文档。

以下示例从 input_1.pdf 中提取第一页,追加 input_2.pdf 的所有页面,并将选定的内容保存为 MergedPdf.pdf:

import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);
  const [isGenerating, setIsGenerating] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');

  useEffect(() => {
    (async () => {
      try {
        const publicUrl = process.env.PUBLIC_URL || '';
        const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.pdf.js`);
        const rawModule = spireModule.default || spireModule;
        window.wasmModule = typeof rawModule === 'function'
          ? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
          : rawModule;
        setWasmModule(window.wasmModule);
      } catch (error) {
        console.error('Failed to load spire.pdf.js:', error);
      }
    })();
  }, []);

  const loadPdfToVfs = async (fileName) => {
    const publicUrl = process.env.PUBLIC_URL || '';
    const response = await fetch(`${publicUrl}/${fileName}`);

    if (!response.ok) {
      throw new Error(`Failed to load ${fileName}: ${response.status}${response.statusText}`);
    }

    const fileBytes = new Uint8Array(await response.arrayBuffer());
    const pdfHeader = String.fromCharCode(...fileBytes.slice(0, 4));

    if (pdfHeader !== '%PDF') {
      throw new Error(`${fileName} was loaded, but it is not a valid PDF file.`);
    }

    window.dotnetRuntime.Module.FS.writeFile(fileName, fileBytes, { flags: 'w+' });
    return fileName;
  };

  const MergePdfs = async () => {
    const wasmModule = window.wasmModule?.spirepdf;
    if (!wasmModule || isGenerating) {
      return;
    }

    setIsGenerating(true);
    setErrorMessage('');

    try {
      const [firstInputFile, secondInputFile] = await Promise.all([
        loadPdfToVfs('input_1.pdf'),
        loadPdfToVfs('input_2.pdf'),
      ]);

      const outputFileName = 'MergedPdf.pdf';
      const firstDocument = new wasmModule.PdfDocument();
      const secondDocument = new wasmModule.PdfDocument();
      const mergedDocument = new wasmModule.PdfDocument();

      firstDocument.LoadFromFile({ fileName: firstInputFile });
      secondDocument.LoadFromFile({ fileName: secondInputFile });

      if (firstDocument.Pages.Count < 1) {
        throw new Error('The first PDF does not contain any pages.');
      }

      if (secondDocument.Pages.Count < 1) {
        throw new Error('The second PDF does not contain any pages.');
      }

      mergedDocument.InsertPage({ ldDoc: firstDocument, pageIndex: 0 });
      mergedDocument.InsertPageRange(secondDocument, 0, secondDocument.Pages.Count - 1);
      mergedDocument.SaveToFile({ fileName: outputFileName });

      const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
      const modifiedFile = new Blob([modifiedFileArray], { type: 'application/pdf' });
      const url = URL.createObjectURL(modifiedFile);
      const a = document.createElement('a');

      a.href = url;
      a.download = outputFileName;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    } catch (error) {
      console.error('Failed to merge PDFs:', error);
      setErrorMessage(error.message || 'Failed to merge PDFs.');
    } finally {
      setIsGenerating(false);
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Merge PDF Documents in React</h1>
      <button onClick={MergePdfs} disabled={!wasmModule || isGenerating}>
        {isGenerating ? 'Generating...' : 'Generate'}
      </button>
      {errorMessage && <p style={{ color: 'crimson' }}>{errorMessage}</p>}
    </div>
  );
}

export default App;

输出:

Merge Selected Pages from Different PDF Documents

理解页面选择逻辑

三个 PdfDocument 实例具有不同的角色:

  • firstDocument 表示 input_1.pdf。
  • secondDocument 表示 input_2.pdf。
  • mergedDocument 是接收选定页面的新 PDF。

在本示例中,PDF 页面索引从 0 开始。因此,pageIndex: 0 指的是第一页:

mergedDocument.InsertPage({ ldDoc: firstDocument, pageIndex: 0 });

以下语句从 secondDocument 中插入连续范围。其起始索引为 0,结束索引为 secondDocument.Pages.Count - 1,因此会追加整个文档:

mergedDocument.InsertPageRange(
  secondDocument,
  0,
  secondDocument.Pages.Count - 1
);

您可以更改这些索引,以仅合并应用程序所需的页面。例如,以下语句从 secondDocument 中插入第 2 至第 5 页,因为其从 0 开始的索引为 1 至 4:

mergedDocument.InsertPageRange(secondDocument, 1, 4);

在使用固定的页面索引之前,请确保源文档包含足够的页面。示例中已检查了空 PDF,但生产级应用程序还应对用户提供的起始和结束索引依据 Pages.Count 进行验证。

重要实现注意事项

保持运行时和输入路径正确

存储在 React public 目录中的文件在运行时通过 URL 请求。代码使用 process.env.PUBLIC_URL,以便在应用程序部署到非根公共路径下时能够正确构建路径。文件路径缺失或不正确可能返回 HTML 错误页面而非 PDF,这就是示例在将数据写入虚拟文件系统之前验证 %PDF 标头的原因。

等待 WebAssembly 初始化

Spire.PDF 在其运行时完全加载之前无法处理文档。wasmModule 状态控制按钮的禁用状态,而 isGenerating 可防止在当前合并完成之前重复启动同一操作。

验证页面范围

当动态选择页面时,请检查起始和结束索引是否为非负数、起始索引是否不超过结束索引,以及两个值是否都在源文档的页数范围内。这样可以避免无效范围错误,并便于在 React 界面中显示有用的消息。

释放下载 URL

URL.createObjectURL() 为生成的 Blob 创建一个临时 URL。在下载开始后调用 URL.revokeObjectURL(url) 可释放该 URL,防止其在浏览器内存中停留过长时间。

结论

Spire.PDF for JavaScript 使 React 应用程序能够通过基于 WebAssembly 的工作流合并 PDF 内容。当需要所有页面时,PdfMerger.Merge() 提供了一种简洁的方式来按定义好的顺序合并多个完整文档。当输出必须仅包含特定内容时,PdfDocument、InsertPage() 和 InsertPageRange() 提供了对结果的页面级控制。

将运行时文件配置在 public 目录后,这些技术可以集成到文档门户、报告工具、合同工作流以及其他需要在浏览器中直接组装 PDF 的 React 应用程序中。