Discuz! Board

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 55|回复: 5

office 二次开发

[复制链接]

412

主题

1281

帖子

4157

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4157
发表于 2025-7-25 20:52:12 | 显示全部楼层 |阅读模式
node.js
yo office
npm start
回复

使用道具 举报

412

主题

1281

帖子

4157

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4157
 楼主| 发表于 2025-7-25 20:54:34 | 显示全部楼层
下面是在PowerPoint顶部创建单独插件栏的完整开发步骤:

1. 环境准备
安装Node.js (建议最新LTS版本)

安装Yeoman和Office Add-in生成器:

bash
npm install -g yo generator-office
2. 创建项目
运行生成器创建新项目:

bash
yo office
选择项目选项:

项目类型:Office Add-in

脚本类型:JavaScript

支持的Office应用:PowerPoint

项目名称:输入你的项目名

是否创建新目录:是
回复

使用道具 举报

412

主题

1281

帖子

4157

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4157
 楼主| 发表于 2025-7-25 20:59:24 | 显示全部楼层
初始文件
/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/

/* global document, Office */

Office.onReady((info) => {
  if (info.host === Office.HostType.PowerPoint) {
    document.getElementById("sideload-msg").style.display = "none";
    document.getElementById("app-body").style.display = "flex";
    document.getElementById("run").onclick = run;
  }
});

export async function run() {
  /**
   * Insert your PowerPoint code here
   */
  const options: Office.SetSelectedDataOptions = { coercionType: Office.CoercionType.Text };
  await Office.context.document.setSelectedDataAsync(" ", options);
  await Office.context.document.setSelectedDataAsync("Hello World!", options);
}
回复

使用道具 举报

412

主题

1281

帖子

4157

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4157
 楼主| 发表于 2025-7-25 21:37:48 | 显示全部楼层
从txt读取并显示:
/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/

/* global document, Office */

Office.onReady((info) => {
  if (info.host === Office.HostType.PowerPoint) {
    document.getElementById("sideload-msg").style.display = "none";
    document.getElementById("app-body").style.display = "flex";
    document.getElementById("run").onclick = run;
  }
});

async function readTextFile(file: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (event) => resolve(event.target?.result as string);
    reader.onerror = (error) => reject(error);
    reader.readAsText(file);
  });
}

export async function run() {
  const fileInput = document.getElementById("txtFile") as HTMLInputElement;
  
  if (!fileInput.files || fileInput.files.length === 0) {
    alert("Please select a TXT file first");
    return;
  }

  try {
    const file = fileInput.files[0];
    const fileContent = await readTextFile(file);
   
    const options: Office.SetSelectedDataOptions = {
      coercionType: Office.CoercionType.Text
    };
   
    // Clear any existing selection first
    await Office.context.document.setSelectedDataAsync(" ", options);
    // Insert the file content
    await Office.context.document.setSelectedDataAsync(fileContent, options);
   
    alert("Text inserted successfully!");
  } catch (error) {
    console.error("Error:", error);
    alert("Error inserting text: " + error.message);
  }
}
回复

使用道具 举报

412

主题

1281

帖子

4157

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4157
 楼主| 发表于 2025-7-26 16:59:27 | 显示全部楼层
回复

使用道具 举报

412

主题

1281

帖子

4157

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4157
 楼主| 发表于 2025-7-26 23:32:29 | 显示全部楼层
Office.onReady((info) => {
  if (info.host === Office.HostType.PowerPoint) {
    document.getElementById("sideload-msg").style.display = "none";
    document.getElementById("app-body").style.display = "flex";
    document.getElementById("run").onclick = run;
  }
});

async function readTextFile(file: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (event) => resolve(event.target?.result as string);
    reader.onerror = (error) => reject(error);
    reader.readAsText(file);
  });
}
function updateStatus(message: string) {
  const statusBox = document.getElementById("statusBox");
  if (statusBox) {
    statusBox.innerHTML = statusBox.innerHTML+`${new Date().toLocaleTimeString()}: ${message}<br>`;
    // 自动滚动到底部
    statusBox.scrollTop = statusBox.scrollHeight;
  }
}

export async function run() {
  document.getElementById("statusBox").innerHTML ="";
  const fileInput = document.getElementById("txtFile") as HTMLInputElement;
  
  if (!fileInput.files || fileInput.files.length === 0) {
    alert("Please select a TXT file first");
    return;
  }

  //try {
    const file = fileInput.files[0];
    const fileContent = await readTextFile(file);
    updateStatus("here2");
    // Parse the content
    const titleRegex = /<title>(.*?)<\/title>/g;
    const paragraphRegex = /<p>(.*?)<\/p>/g;
   
    let titles = [];
    let titleMatch;
        let originalCount=0
    while ((titleMatch = titleRegex.exec(fileContent)) !== null) {
      titles.push({
        text: titleMatch[1],
        position: titleRegex.lastIndex
      });
    }
    updateStatus("here1");
    // Get the PowerPoint context
    await PowerPoint.run(async (context) => {
      // Process each title section
      for (let i = 0; i < titles.length; i++) {
                // Add a new slide (using blank layout)
                const slides = context.presentation.slides;
                const newSlideProxy = slides.add();
                originalCount+=1
                // 3. 强制同步并重新加载全部数据
                await context.sync(); // 第二次同步

                // 4. 通过索引精确获取新幻灯片(绝对可靠)
                const slide = slides.getItemAt(originalCount); // 新幻灯片索引=原总数
                // 2. 加载所有形状(包括隐藏的占位符)
                  slide.load("shapes/items");
                  await context.sync();
                const shapes = slide.shapes.items;
                  for (let i = shapes.length - 1; i >= 0; i--) {
                        shapes[i].delete();
                  }
       
                await context.sync(); // 第三次同步               
                updateStatus(String(originalCount));
   
        // Add title shape
        const titleShape = slide.shapes.addTextBox(titles[i].text);

                updateStatus(titles[i].text);
                await context.sync();
        titleShape.left = 20;
        titleShape.top = 50;
                titleShape.width = 600;  // 设置宽度
                titleShape.height = 50;  // 设置高度
        titleShape.textFrame.textRange.font.size = 24;
        titleShape.textFrame.textRange.font.color = "#1E46EB";
        titleShape.textFrame.textRange.font.name = "KaiTi";
        
        // Find paragraphs between this title and next title
        const startPos = titles[i].position;
        const endPos = i < titles.length - 1 ? titles[i + 1].position : fileContent.length;
        const sectionContent = fileContent.substring(startPos, endPos);
        
        let paragraphMatch;
        let paragraphY = 100;
        while ((paragraphMatch = paragraphRegex.exec(sectionContent)) !== null) {
          const contentShape = slide.shapes.addTextBox(paragraphMatch[1]);
          contentShape.left = 60;
          contentShape.top = paragraphY;
                  contentShape.width = 600;  // 设置宽度
                  contentShape.height = 50;  // 设置高度
          contentShape.textFrame.textRange.font.size = 24;
          contentShape.textFrame.textRange.font.color = "#000000";
          contentShape.textFrame.textRange.font.name = "KaiTi";
          //contentShape.textFrame.paragraphFormat.lineSpacing = 60;
         
          paragraphY += 60;
        }
               
                slide.load("shapes");
                 await context.sync();

               
                  // 4. 绘制坐标轴(使用线条和文本框)
                const axisColor = "#000000"; // 黑色坐标轴
                const axisWidth = 2; // 线宽

                // 绘制X轴
                const xAxis = slide.shapes.addLine("Straight", {
        left: 100,   // 形状容器左边界位置(磅)
    top: 300,    // 形状容器上边界位置(磅)
    width: 400,  // 线条宽度(X轴长度)
    height: 0    // 直线高度设为0
  });
   //3. 设置线条样式(通过lineFormat)
                    xAxis.lineFormat.load("color/type, weight, dashStyle, beginArrowheadStyle, endArrowheadStyle");
                 await context.sync();
  xAxis.lineFormat.weight = 5;          // 线宽(磅)
  xAxis.lineFormat.color = "000000"; // 黑色
  const arrow = slide.shapes.addGeometricShape("Triangle", { left: 300, top: 95, width: 5, height: 50, rotation:60});
  arrow.fill.setSolidColor("000000");
                // 绘制Y轴
                /*const yAxis = slide.shapes.addLine(200, 100, 200, 400, {
                stroke: {
                color: axisColor,
                width: axisWidth,
                dashStyle: "solid"
                }
                });*/
      }
      
      await context.sync();
    });
   
    alert("Presentation created successfully!");
  /*} catch (error) {
    //updateStatus("Error:", error);
    alert("Error creating presentation: " + error.message);
  }*/
}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|DiscuzX

GMT+8, 2025-9-13 17:35 , Processed in 0.038366 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表