'commit'
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,20 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\",
|
||||
"Documents": [],
|
||||
"Documents": [
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
}
|
||||
],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
@@ -9,11 +22,46 @@
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": -1,
|
||||
"SelectedChildIndex": 1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "AiRibbon.cs",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\AiRibbon.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeToolTip": "WordAddIn\\AiRibbon.cs",
|
||||
"ViewState": "AgIAACMAAAAAAAAAAAAowD8AAAAIAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:10.056Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:02.111Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\OutlineSettingsForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:04:59.465Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\",
|
||||
"Documents": [],
|
||||
"Documents": [
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
}
|
||||
],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
@@ -9,11 +22,46 @@
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": -1,
|
||||
"SelectedChildIndex": 1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "AiRibbon.cs",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\AiRibbon.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeToolTip": "WordAddIn\\AiRibbon.cs",
|
||||
"ViewState": "AgIAACMAAAAAAAAAAAAowD8AAAAIAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:10.056Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:02.111Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\OutlineSettingsForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:04:59.465Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,21 +16,44 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace WordAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// Word 插件的主入口类,负责处理 Ribbon 功能区的交互逻辑。
|
||||
/// 实现了 IRibbonExtensibility 接口,用于自定义 Word 的功能区界面。
|
||||
/// 包含了所有 Ribbon 按钮的点击事件处理方法。
|
||||
/// </summary>
|
||||
[ComVisible(true)]
|
||||
public class AiRibbon : Office.IRibbonExtensibility
|
||||
{
|
||||
// Ribbon UI 实例引用,用于刷新界面等操作
|
||||
private Office.IRibbonUI ribbon;
|
||||
private AiService _aiService = new AiService();
|
||||
private static bool _isLoggedIn = false; // 登录状态 (静态变量,确保整个应用程序生命周期内有效)
|
||||
private string _documentContext = null; // 存储文档的全文分析上下文
|
||||
private string _referenceContext = null; // 存储外部参考文档内容
|
||||
|
||||
// AI 服务实例,封装了所有与后端 API 的通信逻辑
|
||||
private AiService _aiService = new AiService();
|
||||
|
||||
// 登录状态标识 (静态变量,确保在插件生命周期内保持状态)
|
||||
private static bool _isLoggedIn = false;
|
||||
|
||||
// 存储文档的全文分析上下文 (通过"通读全文"功能生成),用于辅助 AI 理解文档背景
|
||||
private string _documentContext = null;
|
||||
|
||||
// 存储外部参考文档内容 (通过"补充背景"功能导入),用于辅助 AI 进行写作
|
||||
private string _referenceContext = null;
|
||||
|
||||
/// <summary>
|
||||
/// 默认构造函数
|
||||
/// </summary>
|
||||
public AiRibbon()
|
||||
{
|
||||
}
|
||||
|
||||
#region IRibbonExtensibility 成员
|
||||
|
||||
/// <summary>
|
||||
/// 加载自定义 Ribbon XML 界面定义。
|
||||
/// 该方法由 VSTO 运行时调用。
|
||||
/// </summary>
|
||||
/// <param name="ribbonID">Ribbon ID</param>
|
||||
/// <returns>包含 Ribbon 定义的 XML 字符串</returns>
|
||||
public string GetCustomUI(string ribbonID)
|
||||
{
|
||||
return GetResourceText("WordAddIn.AiRibbon.xml");
|
||||
@@ -40,11 +63,22 @@ namespace WordAddIn
|
||||
|
||||
#region Ribbon 回调
|
||||
|
||||
/// <summary>
|
||||
/// Ribbon 加载完成时的回调方法。
|
||||
/// 保存 Ribbon UI 实例引用,以便后续可以使用它来刷新 Ribbon 控件的状态。
|
||||
/// </summary>
|
||||
/// <param name="ribbonUI">Ribbon UI 实例</param>
|
||||
public void Ribbon_Load(Office.IRibbonUI ribbonUI)
|
||||
{
|
||||
this.ribbon = ribbonUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取组合的上下文信息,包含全文背景和外部参考资料。
|
||||
/// 将 _documentContext (全文背景) 和 _referenceContext (外部参考) 拼接成一个字符串。
|
||||
/// 用于发送给 AI 以提供更准确的生成结果,确保生成内容与文档上下文和参考资料保持一致。
|
||||
/// </summary>
|
||||
/// <returns>组合后的上下文描述字符串</returns>
|
||||
private string GetCombinedContext()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -63,13 +97,20 @@ namespace WordAddIn
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【补充背景】按钮点击事件处理。
|
||||
/// 允许用户导入外部文档 (txt, doc, docx) 作为背景资料。
|
||||
/// 读取的文件内容将存储在 _referenceContext 中,供后续 AI 生成任务使用。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public void OnImportReferenceClick(Office.IRibbonControl control)
|
||||
{
|
||||
// 检查用户是否已登录,未登录则弹出登录框
|
||||
if (!EnsureLoggedIn()) return;
|
||||
|
||||
using (OpenFileDialog openFileDialog = new OpenFileDialog())
|
||||
{
|
||||
openFileDialog.Multiselect = true;
|
||||
openFileDialog.Multiselect = true; // 允许选择多个文件
|
||||
openFileDialog.Title = "补充背景 - 导入文档";
|
||||
openFileDialog.Filter = "文档文件|*.docx;*.doc;*.txt|Word 文档|*.docx;*.doc|文本文件|*.txt|所有文件|*.*";
|
||||
|
||||
@@ -90,6 +131,7 @@ namespace WordAddIn
|
||||
}
|
||||
else if (ext == ".docx" || ext == ".doc")
|
||||
{
|
||||
// 使用 Word Interop 打开并读取文档内容
|
||||
dynamic wordApp = Globals.ThisAddIn.Application;
|
||||
dynamic doc = wordApp.Documents.Open(FileName: file, Visible: false, ReadOnly: true, AddToRecentFiles: false);
|
||||
content = doc.Content.Text;
|
||||
@@ -119,6 +161,12 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【通读全文】按钮点击事件处理。
|
||||
/// 读取当前文档的全部文本内容,调用 AI 进行深度语义分析。
|
||||
/// 生成的文档画像(包括摘要、风格、术语等)将存储在 _documentContext 中。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public async void OnReadFullClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
@@ -145,7 +193,7 @@ namespace WordAddIn
|
||||
fullText = fullText.Substring(0, 20000);
|
||||
}
|
||||
|
||||
// 1. 语义分析
|
||||
// 调用 AI 服务进行文档分析
|
||||
_documentContext = await Task.Run(() => _aiService.AnalyzeDocument(fullText));
|
||||
|
||||
if (!string.IsNullOrEmpty(_documentContext))
|
||||
@@ -167,6 +215,14 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【润色加工】按钮点击事件处理。
|
||||
/// 功能描述:
|
||||
/// 1. 获取选中文本(或剪贴板内容)。
|
||||
/// 2. 调用 AI 润色接口,优化语言流畅度和专业性。
|
||||
/// 3. 将润色后的文本替换回文档。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public async void OnPolishClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
@@ -189,12 +245,7 @@ namespace WordAddIn
|
||||
string clipboardText = (string)data.GetData(DataFormats.Text);
|
||||
if (!string.IsNullOrWhiteSpace(clipboardText))
|
||||
{
|
||||
// 询问用户是否要润色剪贴板内容
|
||||
// 简单起见,如果没选中文字,默认就是处理剪贴板(类似扩写逻辑)
|
||||
// 但为了避免误操作,我们可以提示一下?
|
||||
// 扩写逻辑是直接把剪贴板当 originalText,生成后插入光标处。
|
||||
// 润色通常是“替换”原文。如果是剪贴板,那就是“插入润色后的内容”。
|
||||
// 逻辑一致。
|
||||
// 剪贴板有内容,逻辑继续
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -231,11 +282,12 @@ namespace WordAddIn
|
||||
loading.Show();
|
||||
loading.Refresh();
|
||||
|
||||
// 在后台线程运行 AI 请求
|
||||
// 3. 在后台线程运行 AI 请求,传入原文和组合上下文
|
||||
string polishedText = await Task.Run(() => _aiService.PolishText(originalText, GetCombinedContext()));
|
||||
|
||||
if (!string.IsNullOrEmpty(polishedText))
|
||||
{
|
||||
// 4. 使用智能插入替换原文
|
||||
SmartInsert(selection, polishedText);
|
||||
}
|
||||
}
|
||||
@@ -253,6 +305,14 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【深度升华】按钮点击事件处理。
|
||||
/// 功能描述:
|
||||
/// 1. 获取选中文本。
|
||||
/// 2. 调用 AI 升华接口,挖掘思想深度,提升价值。
|
||||
/// 3. 将升华后的文本插入文档。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public async void OnElevateClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
@@ -262,7 +322,7 @@ namespace WordAddIn
|
||||
{
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
|
||||
// 1. 判断是否有选中文本
|
||||
// 1. 判断是否有选中文本或剪贴板内容
|
||||
bool hasSelection = selection.Type == Word.WdSelectionType.wdSelectionNormal ||
|
||||
(!string.IsNullOrEmpty(selection.Text) && selection.Text.Length > 1);
|
||||
|
||||
@@ -302,16 +362,16 @@ namespace WordAddIn
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载窗体
|
||||
loading = new LoadingForm("AI 正在深度升华中,挖掘核心价值...");
|
||||
loading.Show();
|
||||
loading.Refresh();
|
||||
|
||||
// 在后台线程运行 AI 请求
|
||||
// 2. 调用 AI 服务进行深度升华
|
||||
string elevatedText = await Task.Run(() => _aiService.ElevateText(originalText, GetCombinedContext()));
|
||||
|
||||
if (!string.IsNullOrEmpty(elevatedText))
|
||||
{
|
||||
// 3. 智能插入结果
|
||||
SmartInsert(selection, elevatedText);
|
||||
}
|
||||
}
|
||||
@@ -329,6 +389,15 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【智能扩写】按钮点击事件处理。
|
||||
/// 功能描述:
|
||||
/// 1. 获取用户选中的文本(或剪贴板内容)。
|
||||
/// 2. 弹出选项窗体,允许用户输入自定义扩写提示词。
|
||||
/// 3. 调用 AI 扩写接口,传入原文、用户提示和全文上下文。
|
||||
/// 4. 将 AI 生成的扩写内容替换原有选区,或插入到光标位置。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public async void OnExpandClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
@@ -339,6 +408,7 @@ namespace WordAddIn
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
|
||||
// 1. 判断是否有选中文本
|
||||
// wdSelectionNormal 表示有选区,否则检查 Text 属性
|
||||
bool hasSelection = selection.Type == Word.WdSelectionType.wdSelectionNormal ||
|
||||
(!string.IsNullOrEmpty(selection.Text) && selection.Text.Length > 1);
|
||||
|
||||
@@ -349,46 +419,7 @@ namespace WordAddIn
|
||||
if (data != null && data.GetDataPresent(DataFormats.Text))
|
||||
{
|
||||
string clipboardText = (string)data.GetData(DataFormats.Text);
|
||||
if (!string.IsNullOrWhiteSpace(clipboardText))
|
||||
{
|
||||
// 将剪贴板内容插入文档,并选中它作为待扩写内容
|
||||
selection.Range.Text = clipboardText;
|
||||
// 重新获取 selection,确保包含刚插入的文本
|
||||
// 注意:赋值 Range.Text 后,selection 通常会变成插入文本后的光标,或者选中插入文本
|
||||
// 我们显式选中刚才插入的范围比较稳妥
|
||||
// 但简化处理,直接让用户确认是否扩写刚才粘贴的内容
|
||||
// 这里 selection.Text = clipboardText 会替换光标或选区
|
||||
// 此时 selection 可能位于文本末尾。
|
||||
// 为了扩写,我们需要选中这段新文本。
|
||||
// 简单起见,我们直接把 clipboardText 作为 originalText 传给 AI,
|
||||
// 但回写时需要知道覆盖哪里。
|
||||
// 最好的方式是:先插入,让 Selection 包含这段文字。
|
||||
|
||||
// 实际上 Word 的 Selection.Text = "xxx" 后,Selection 会变成折叠状态在文字后面。
|
||||
// 所以我们需要倒推回去选中它。
|
||||
// 或者,我们可以直接记录 Range。
|
||||
|
||||
// 修正策略:直接用 clipboardText 发给 AI,
|
||||
// 然后生成完后,直接在这个位置插入(Selection 还在原处或刚插入文本的后面)。
|
||||
// 但原来的逻辑是 selection.Text = expandedText (替换)。
|
||||
// 如果我刚才没插入,现在插入 expandedText 也是对的。
|
||||
// 唯一的问题是:加载动画期间,用户看不到原文。
|
||||
// 但这也许是好事?直接变身。
|
||||
// 让我们采用“直接替换”逻辑,不先插入原文。
|
||||
|
||||
// 但等等,如果是“扩写”,通常是基于原文。
|
||||
// 如果不插入,用户就没法在扩写前对原文做微调(虽然现在的流程也不支持微调)。
|
||||
// 既然用户点击了按钮,就是想一步到位。
|
||||
|
||||
// 所以:如果没选中,就假装选中了剪贴板内容。
|
||||
// originalText = clipboardText
|
||||
// 后面 selection.Text = expandedText 会直接在光标处插入扩写后的内容。
|
||||
// 完美。
|
||||
|
||||
// 唯一例外:如果 selection 是光标,selection.Text = expandedText 会插入。
|
||||
// 此时 originalText = clipboardText.
|
||||
}
|
||||
else
|
||||
if (string.IsNullOrWhiteSpace(clipboardText))
|
||||
{
|
||||
MessageBox.Show("剪贴板中内容为空,请先选择一段需要扩写的文字,或者复制文字到剪贴板。");
|
||||
return;
|
||||
@@ -401,10 +432,6 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
// 此时 selection.Text 可能为空(如果是剪贴板模式),
|
||||
// 所以我们不能再依赖 selection.Text.Trim() 作为 originalText
|
||||
// 我们需要一个变量来持有它。
|
||||
|
||||
string originalText;
|
||||
if (hasSelection)
|
||||
{
|
||||
@@ -412,7 +439,7 @@ namespace WordAddIn
|
||||
}
|
||||
else
|
||||
{
|
||||
// 再次获取,确保逻辑严谨(虽然上面已经 check 过了)
|
||||
// 使用剪贴板内容
|
||||
IDataObject data = Clipboard.GetDataObject();
|
||||
originalText = (string)data.GetData(DataFormats.Text);
|
||||
}
|
||||
@@ -423,7 +450,7 @@ namespace WordAddIn
|
||||
return;
|
||||
}
|
||||
|
||||
// 弹出选项设置窗体
|
||||
// 3. 弹出选项设置窗体,让用户输入自定义扩写提示词
|
||||
string customPrompt = null;
|
||||
using (var optionsForm = new ExpandOptionsForm())
|
||||
{
|
||||
@@ -434,17 +461,17 @@ namespace WordAddIn
|
||||
customPrompt = optionsForm.SelectedPrompt;
|
||||
}
|
||||
|
||||
// 显示加载窗体
|
||||
loading = new LoadingForm("AI 正在扩写中,请稍候...");
|
||||
loading.Show();
|
||||
loading.Refresh(); // 强制重绘
|
||||
loading.Refresh();
|
||||
|
||||
// 在后台线程运行 AI 请求,防止阻塞 UI
|
||||
// 4. 在后台线程运行 AI 请求,防止阻塞 UI
|
||||
// 传入 GetCombinedContext() 以利用文档背景和参考资料
|
||||
string expandedText = await Task.Run(() => _aiService.ExpandText(originalText, customPrompt, GetCombinedContext()));
|
||||
|
||||
if (!string.IsNullOrEmpty(expandedText))
|
||||
{
|
||||
// 使用智能插入代替直接赋值
|
||||
// 5. 智能插入:尝试保持格式
|
||||
SmartInsert(selection, expandedText);
|
||||
}
|
||||
}
|
||||
@@ -462,6 +489,15 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【素材写作】按钮点击事件处理。
|
||||
/// 功能描述:
|
||||
/// 1. 获取选中文本作为上下文(可选)。
|
||||
/// 2. 弹出素材写作配置窗体,允许用户输入文本素材或上传文档素材。
|
||||
/// 3. 解析上传的文件内容(支持 .txt, .doc, .docx)。
|
||||
/// 4. 调用 AI 接口,根据素材生成新的内容。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public async void OnReferenceGenerateClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
@@ -470,11 +506,12 @@ namespace WordAddIn
|
||||
try
|
||||
{
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
string originalText = selection.Text; // 可以为空
|
||||
string originalText = selection.Text; // 可以为空,作为生成的上下文参考
|
||||
|
||||
string referenceContent = null;
|
||||
int wordLimit = 500;
|
||||
|
||||
// 1. 弹出素材写作配置窗体
|
||||
using (var form = new ReferenceGeneratorForm())
|
||||
{
|
||||
if (form.ShowDialog() != DialogResult.OK)
|
||||
@@ -484,8 +521,10 @@ namespace WordAddIn
|
||||
|
||||
wordLimit = form.WordLimit;
|
||||
|
||||
// 2. 处理素材内容
|
||||
if (form.IsFileMode)
|
||||
{
|
||||
// 文件模式:读取文件内容
|
||||
string path = form.SelectedFilePath;
|
||||
string ext = Path.GetExtension(path).ToLower();
|
||||
|
||||
@@ -495,10 +534,9 @@ namespace WordAddIn
|
||||
}
|
||||
else
|
||||
{
|
||||
// 尝试用 Word 打开
|
||||
// 尝试用 Word Interop 打开读取 (针对 .doc, .docx)
|
||||
try
|
||||
{
|
||||
// 使用 dynamic 避免繁琐的 ref object
|
||||
dynamic wordApp = Globals.ThisAddIn.Application;
|
||||
dynamic doc = wordApp.Documents.Open(FileName: path, Visible: false, ReadOnly: true, AddToRecentFiles: false);
|
||||
referenceContent = doc.Content.Text;
|
||||
@@ -513,6 +551,7 @@ namespace WordAddIn
|
||||
}
|
||||
else
|
||||
{
|
||||
// 文本模式:直接获取输入的文本
|
||||
referenceContent = form.ReferenceContent;
|
||||
}
|
||||
}
|
||||
@@ -527,11 +566,12 @@ namespace WordAddIn
|
||||
loading.Show();
|
||||
loading.Refresh();
|
||||
|
||||
// 3. 调用 AI 服务进行生成
|
||||
// 传入原文(上下文)、参考素材、字数限制、全文背景
|
||||
string result = await Task.Run(() => _aiService.GenerateWithReference(originalText, referenceContent, wordLimit, GetCombinedContext()));
|
||||
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
{
|
||||
// 使用智能插入代替直接赋值
|
||||
SmartInsert(selection, result);
|
||||
}
|
||||
}
|
||||
@@ -549,21 +589,18 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 智能插入辅助方法。
|
||||
/// 使用 TypeText 方法插入文本,以保持当前光标处的格式。
|
||||
/// 如果 TypeText 失败,则回退到直接赋值 Text 属性。
|
||||
/// </summary>
|
||||
/// <param name="selection">Word 选区对象</param>
|
||||
/// <param name="text">要插入的文本</param>
|
||||
private void SmartInsert(Word.Selection selection, string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 简化逻辑:直接使用 TypeText 插入,让 Word 自动应用当前光标位置的格式
|
||||
// 这样可以最大程度保持与原文格式一致(兼容性最好)
|
||||
// 同时也避免了手动应用样式带来的偏差
|
||||
|
||||
// 如果是覆盖选区,先检查是否需要清空(TypeText 会自动替换选区,但显式处理更清晰)
|
||||
// selection.TypeText(text); // 这就足够了
|
||||
|
||||
// 2024-01-24 修正:用户反馈格式偏差大,且不希望 Markdown。
|
||||
// 因此我们不再进行 Markdown 解析,也不再强制应用 StyleProfile 中的样式。
|
||||
// 直接插入纯文本,让 Word 处理。
|
||||
|
||||
// 使用 TypeText 插入,让 Word 自动应用当前光标位置的格式
|
||||
selection.TypeText(text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -574,6 +611,15 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【格式美化】按钮点击事件处理。
|
||||
/// 功能描述:
|
||||
/// 1. 读取当前文档全文。
|
||||
/// 2. 调用 AI 分析文档结构,识别各级标题。
|
||||
/// 3. 遍历文档段落,根据 AI 返回的标题列表,将匹配的段落设置为对应的标题样式 (Heading 1-5)。
|
||||
/// 4. 对非标题的正文段落应用首行缩进。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public async void OnFormatClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
@@ -584,13 +630,14 @@ namespace WordAddIn
|
||||
var doc = Globals.ThisAddIn.Application.ActiveDocument;
|
||||
string fullText = doc.Content.Text;
|
||||
|
||||
// 1. 基础校验:文档内容太少则不处理
|
||||
if (string.IsNullOrWhiteSpace(fullText) || fullText.Length < 10)
|
||||
{
|
||||
MessageBox.Show("文档内容过少,无法进行格式美化。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 限制长度防止 Token 溢出
|
||||
// 2. 截断处理:限制长度防止 Token 溢出 (保留前 20000 字符进行结构分析)
|
||||
if (fullText.Length > 20000)
|
||||
{
|
||||
fullText = fullText.Substring(0, 20000);
|
||||
@@ -600,17 +647,18 @@ namespace WordAddIn
|
||||
loading.Show();
|
||||
loading.Refresh();
|
||||
|
||||
// 1. AI 分析结构
|
||||
// 3. AI 分析结构:识别标题及其层级
|
||||
// 这一步是关键,利用大模型理解语义来判断哪些行是标题,比正则更准确
|
||||
var headings = await Task.Run(() => _aiService.AnalyzeStructure(fullText));
|
||||
|
||||
// 即使 headings 为空,我们也要继续处理正文缩进,所以这里不再直接 return
|
||||
if (headings == null)
|
||||
{
|
||||
headings = new List<WordAddIn.AiService.HeadingInfo>();
|
||||
// MessageBox.Show("未识别到有效的标题结构。"); // 可以不提示,静默处理
|
||||
}
|
||||
|
||||
// 2. 应用样式 (在 UI 线程)
|
||||
// 4. 应用样式 (在 UI 线程操作 Word 对象)
|
||||
// 遍历所有段落,性能可能受文档长度影响,但对于一般文档可接受
|
||||
int changeCount = 0;
|
||||
int indentCount = 0;
|
||||
foreach (Word.Paragraph p in doc.Paragraphs)
|
||||
@@ -618,13 +666,13 @@ namespace WordAddIn
|
||||
string pText = p.Range.Text.Trim();
|
||||
if (string.IsNullOrWhiteSpace(pText)) continue;
|
||||
|
||||
// 查找匹配的标题
|
||||
// AI 返回的 text 可能不包含序号,或者有微小差异。
|
||||
// 使用 Contains 匹配比较宽容
|
||||
// 5. 查找匹配:检查当前段落文本是否在 AI 识别的标题列表中
|
||||
// 注意:这里使用 Contains 模糊匹配,因为 Word 段落可能包含额外的隐藏字符
|
||||
var match = headings.FirstOrDefault(h => pText.Contains(h.text));
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
// 是标题:设置对应的 Word 内置标题样式
|
||||
Word.WdBuiltinStyle styleEnum = Word.WdBuiltinStyle.wdStyleNormal;
|
||||
switch (match.level)
|
||||
{
|
||||
@@ -638,7 +686,7 @@ namespace WordAddIn
|
||||
|
||||
p.set_Style(styleEnum);
|
||||
|
||||
// 标题通常不需要首行缩进(或者由样式决定),这里显式清除手动缩进,以防万一
|
||||
// 标题通常不需要首行缩进,强制清除
|
||||
p.Format.CharacterUnitFirstLineIndent = 0;
|
||||
p.Format.FirstLineIndent = 0;
|
||||
|
||||
@@ -646,7 +694,8 @@ namespace WordAddIn
|
||||
}
|
||||
else
|
||||
{
|
||||
// 正文段落:设置首行缩进2字符
|
||||
// 6. 非标题(正文):统一设置首行缩进 2 字符
|
||||
// 让文档看起来更规范
|
||||
p.Format.CharacterUnitFirstLineIndent = 2;
|
||||
indentCount++;
|
||||
}
|
||||
@@ -673,6 +722,15 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【配图生成】按钮点击事件处理。
|
||||
/// 功能描述:
|
||||
/// 1. 获取用户选中的文字作为绘图描述 (Prompt)。
|
||||
/// 2. 弹出图片风格设置窗体,让用户选择风格(如写实、卡通、水墨等)和补充要求。
|
||||
/// 3. 调用 AI 绘图接口 (DALL-E 3 / Gemini Image),获取生成图片的 URL。
|
||||
/// 4. 下载图片并插入到 Word 文档中。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public async void OnImageClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
@@ -688,7 +746,7 @@ namespace WordAddIn
|
||||
return;
|
||||
}
|
||||
|
||||
// 弹出图片风格设置窗体
|
||||
// 1. 弹出图片风格设置窗体
|
||||
string style = null;
|
||||
string additionalPrompt = null;
|
||||
using (var imgOptions = new ImageOptionsForm())
|
||||
@@ -701,7 +759,7 @@ namespace WordAddIn
|
||||
additionalPrompt = imgOptions.AdditionalPrompt;
|
||||
}
|
||||
|
||||
// 组合 Prompt
|
||||
// 2. 组合 Prompt:原文 + 补充要求
|
||||
if (!string.IsNullOrWhiteSpace(additionalPrompt))
|
||||
{
|
||||
prompt += $"\n补充要求: {additionalPrompt}";
|
||||
@@ -711,11 +769,13 @@ namespace WordAddIn
|
||||
loading.Show();
|
||||
loading.Refresh();
|
||||
|
||||
// 传入 style 参数
|
||||
// 3. 调用 AI 服务生成图片 URL
|
||||
// 注意:生图通常比较慢,需要耐心等待
|
||||
string imageUrl = await Task.Run(() => _aiService.GenerateImageUrl(prompt, style));
|
||||
|
||||
if (!string.IsNullOrEmpty(imageUrl))
|
||||
{
|
||||
// 4. 插入图片
|
||||
InsertImageFromUrl(imageUrl);
|
||||
}
|
||||
else
|
||||
@@ -725,7 +785,7 @@ namespace WordAddIn
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("图片生成失败: " + ex.Message);
|
||||
MessageBox.Show("配图生成失败: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -737,138 +797,182 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【创建大纲】按钮点击事件处理。
|
||||
/// 功能描述:
|
||||
/// 1. 弹出模态对话框让用户输入大纲主题。
|
||||
/// 2. 调用 AI 服务生成结构化的 JSON 大纲数据。
|
||||
/// 3. 解析 JSON 数据,遍历大纲节点。
|
||||
/// 4. 将大纲内容插入 Word 文档,并根据层级应用预设的字体、字号、对齐方式等样式。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件 (由 VSTO 运行时传入)</param>
|
||||
public async void OnCreateOutlineClick(Office.IRibbonControl control)
|
||||
{
|
||||
// 1. 安全检查:确保用户已登录,否则无法使用 AI 功能
|
||||
if (!EnsureLoggedIn()) return;
|
||||
|
||||
string topic = null;
|
||||
|
||||
// 2. 弹出“创建大纲”输入窗体
|
||||
using (var form = new CreateOutlineForm())
|
||||
{
|
||||
if (form.ShowDialog() != DialogResult.OK) return; // 用户点击取消
|
||||
topic = form.Topic;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(topic)) return;
|
||||
|
||||
// 3. 显示加载提示,提升用户体验
|
||||
LoadingForm loading = new LoadingForm("AI 正在构思大纲结构...");
|
||||
loading.Show();
|
||||
loading.Refresh(); // 强制刷新 UI,确保 loading 窗口立即显示
|
||||
|
||||
try
|
||||
{
|
||||
// 4. 异步调用 AI 服务生成大纲
|
||||
// 使用 Task.Run 将耗时操作放入后台线程,避免阻塞 Word UI 主线程
|
||||
List<OutlineItem> outline = await Task.Run(() => _aiService.GenerateOutline(topic));
|
||||
|
||||
if (outline == null || outline.Count == 0)
|
||||
{
|
||||
MessageBox.Show("未能生成大纲,请重试。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. 获取大纲样式配置
|
||||
// 从用户设置中读取 JSON 字符串并反序列化,如果为空则使用默认样式
|
||||
string styleJson = Settings.Default.OutlineStyles;
|
||||
OutlineStyleConfig styleConfig = string.IsNullOrEmpty(styleJson)
|
||||
? new OutlineStyleConfig()
|
||||
: JsonConvert.DeserializeObject<OutlineStyleConfig>(styleJson);
|
||||
|
||||
var doc = Globals.ThisAddIn.Application.ActiveDocument;
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
Word.Range insertRange = selection.Range;
|
||||
insertRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
|
||||
|
||||
// 6. 遍历生成的大纲项,逐行插入文档
|
||||
foreach (var item in outline)
|
||||
{
|
||||
// 在当前光标位置添加新段落
|
||||
Word.Paragraph p = doc.Content.Paragraphs.Add(insertRange);
|
||||
p.Range.Text = item.Title + "\n";
|
||||
|
||||
// 7. 根据层级应用对应的样式 (字体、大小、加粗、对齐)
|
||||
TextStyle ts = null;
|
||||
switch (item.Level)
|
||||
{
|
||||
case 0: // 主标题
|
||||
ts = styleConfig.MainTitle;
|
||||
p.set_Style(Word.WdBuiltinStyle.wdStyleTitle);
|
||||
break;
|
||||
case 1: // 一级标题
|
||||
ts = styleConfig.Heading1;
|
||||
p.set_Style(Word.WdBuiltinStyle.wdStyleHeading1);
|
||||
break;
|
||||
case 2: // 二级标题
|
||||
ts = styleConfig.Heading2;
|
||||
p.set_Style(Word.WdBuiltinStyle.wdStyleHeading2);
|
||||
break;
|
||||
case 3: // 三级标题
|
||||
ts = styleConfig.Heading3;
|
||||
p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3);
|
||||
break;
|
||||
default:
|
||||
ts = styleConfig.Heading3;
|
||||
break;
|
||||
}
|
||||
|
||||
// 应用自定义样式属性
|
||||
if (ts != null)
|
||||
{
|
||||
p.Range.Font.Name = ts.FontName;
|
||||
p.Range.Font.Size = ts.FontSize;
|
||||
p.Range.Font.Bold = ts.IsBold ? 1 : 0;
|
||||
p.Range.Font.Color = Word.WdColor.wdColorBlack; // 强制设为黑色,避免出现默认蓝色的情况
|
||||
|
||||
if (item.Level == 0) // 仅主标题应用自定义对齐方式
|
||||
{
|
||||
p.Format.Alignment = (Word.WdParagraphAlignment)ts.Alignment;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新插入位置到当前段落之后,准备插入下一行
|
||||
insertRange = p.Range;
|
||||
insertRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 异常处理:捕获并显示错误信息,防止插件崩溃
|
||||
MessageBox.Show("生成大纲失败: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 8. 无论成功与否,都要关闭 Loading 窗口
|
||||
loading.CloseSafe();
|
||||
loading.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 【大纲配置】按钮点击事件处理。
|
||||
/// 打开大纲样式配置窗口,允许用户自定义各级标题的字体、字号等。
|
||||
/// </summary>
|
||||
/// <param name="control">触发事件的 Ribbon 控件</param>
|
||||
public void OnOutlineSettingClick(Office.IRibbonControl control)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
|
||||
using (var form = new OutlineSettingsForm())
|
||||
{
|
||||
form.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
public async void OnCreateOutlineClick(Office.IRibbonControl control)
|
||||
/// <summary>
|
||||
/// 从 URL 插入图片到 Word 文档中。
|
||||
/// 下载图片并保存为临时文件,然后插入到当前光标位置。
|
||||
/// </summary>
|
||||
/// <param name="imageUrl">图片的 URL 地址</param>
|
||||
private void InsertImageFromUrl(string imageUrl)
|
||||
{
|
||||
if (!EnsureLoggedIn()) return;
|
||||
|
||||
string topic = null;
|
||||
using (var form = new CreateOutlineForm())
|
||||
{
|
||||
if (form.ShowDialog() != DialogResult.OK) return;
|
||||
topic = form.Topic;
|
||||
}
|
||||
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
loading = new LoadingForm("AI 正在生成大纲,请稍候...");
|
||||
loading.Show();
|
||||
loading.Refresh();
|
||||
|
||||
var outline = await Task.Run(() => _aiService.GenerateOutline(topic));
|
||||
|
||||
if (outline == null || outline.Count == 0)
|
||||
string tempPath = Path.GetTempFileName() + ".png";
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
MessageBox.Show("生成大纲失败,请重试。");
|
||||
return;
|
||||
client.DownloadFile(imageUrl, tempPath);
|
||||
}
|
||||
|
||||
// Load config
|
||||
OutlineStyleConfig config = null;
|
||||
try
|
||||
{
|
||||
string json = Settings.Default.OutlineStyles;
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
config = JsonConvert.DeserializeObject<OutlineStyleConfig>(json);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
if (config == null) config = new OutlineStyleConfig();
|
||||
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var inlineShape = selection.InlineShapes.AddPicture(FileName: tempPath, LinkToFile: false, SaveWithDocument: true);
|
||||
|
||||
// Move to end of selection to avoid overwriting if just cursor
|
||||
selection.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
|
||||
|
||||
foreach (var item in outline)
|
||||
// 简单的图片调整
|
||||
inlineShape.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoTrue;
|
||||
if (inlineShape.Width > 400)
|
||||
{
|
||||
// Type text
|
||||
selection.TypeText(item.Title);
|
||||
|
||||
// Get current paragraph
|
||||
Word.Paragraph p = selection.Paragraphs[1];
|
||||
|
||||
// 1. Apply Built-in Style
|
||||
try
|
||||
{
|
||||
switch (item.Level)
|
||||
{
|
||||
case 0: p.set_Style(Word.WdBuiltinStyle.wdStyleTitle); break;
|
||||
case 1: p.set_Style(Word.WdBuiltinStyle.wdStyleHeading1); break;
|
||||
case 2: p.set_Style(Word.WdBuiltinStyle.wdStyleHeading2); break;
|
||||
case 3: p.set_Style(Word.WdBuiltinStyle.wdStyleHeading3); break;
|
||||
default: p.set_Style(Word.WdBuiltinStyle.wdStyleNormal); break;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 2. Apply Custom Config
|
||||
TextStyle style = null;
|
||||
switch (item.Level)
|
||||
{
|
||||
case 0: style = config.MainTitle; break;
|
||||
case 1: style = config.Heading1; break;
|
||||
case 2: style = config.Heading2; break;
|
||||
case 3: style = config.Heading3; break;
|
||||
default: style = config.Heading3; break;
|
||||
}
|
||||
|
||||
if (style != null)
|
||||
{
|
||||
p.Range.Font.Name = style.FontName;
|
||||
p.Range.Font.Size = style.FontSize;
|
||||
p.Range.Font.Bold = style.IsBold ? 1 : 0;
|
||||
p.Range.Font.Color = Word.WdColor.wdColorBlack; // 强制设置为黑色
|
||||
|
||||
if (style.Alignment == 1) p.Format.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
|
||||
else if (style.Alignment == 2) p.Format.Alignment = Word.WdParagraphAlignment.wdAlignParagraphRight;
|
||||
else p.Format.Alignment = Word.WdParagraphAlignment.wdAlignParagraphLeft;
|
||||
}
|
||||
|
||||
// New line for next item
|
||||
selection.TypeParagraph();
|
||||
inlineShape.Width = 400; // 限制最大宽度
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("创建大纲失败: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (loading != null)
|
||||
{
|
||||
loading.CloseSafe();
|
||||
loading.Dispose();
|
||||
}
|
||||
MessageBox.Show("插入图片失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSettingsClick(Office.IRibbonControl control)
|
||||
{
|
||||
MessageBox.Show("请在代码 AiService.cs 中配置您的 API Key。", "设置");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 帮助器
|
||||
|
||||
/// <summary>
|
||||
/// 确保用户已登录。
|
||||
/// 检查 _isLoggedIn 状态,如果未登录则弹出登录窗口。
|
||||
/// </summary>
|
||||
/// <returns>如果已登录或登录成功返回 true,否则返回 false</returns>
|
||||
private bool EnsureLoggedIn()
|
||||
{
|
||||
if (_isLoggedIn) return true;
|
||||
|
||||
using (var loginForm = new LoginForm())
|
||||
using (LoginForm login = new LoginForm())
|
||||
{
|
||||
if (loginForm.ShowDialog() == DialogResult.OK)
|
||||
if (login.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
_isLoggedIn = true;
|
||||
return true;
|
||||
@@ -877,58 +981,12 @@ namespace WordAddIn
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InsertImageFromUrl(string imageUrl)
|
||||
{
|
||||
string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".png");
|
||||
try
|
||||
{
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
client.DownloadFile(imageUrl, tempFile);
|
||||
}
|
||||
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
// 移动到选区末尾插入,避免覆盖文字
|
||||
selection.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
|
||||
selection.TypeParagraph(); // 将图片嵌入文档
|
||||
var inlineShape = selection.InlineShapes.AddPicture(tempFile, LinkToFile: false, SaveWithDocument: true);
|
||||
|
||||
// 自动调整图片大小为页面宽度的 80% (留出一点边距更美观)
|
||||
try
|
||||
{
|
||||
var pageSetup = selection.PageSetup;
|
||||
float pageWidth = pageSetup.PageWidth;
|
||||
float leftMargin = pageSetup.LeftMargin;
|
||||
float rightMargin = pageSetup.RightMargin;
|
||||
|
||||
// 有效内容宽度
|
||||
float contentWidth = pageWidth - leftMargin - rightMargin;
|
||||
|
||||
// 设置图片宽度 (如果图片本身小于页面宽度,也可以选择不放大,这里强制设为 100% 宽度)
|
||||
inlineShape.Width = contentWidth;
|
||||
|
||||
// 保持纵横比
|
||||
inlineShape.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoTrue;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果获取页面设置失败,给一个默认值 (500磅,约17.6cm,接近A4宽度)
|
||||
inlineShape.Width = 500;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("图片下载或插入失败: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
try { File.Delete(tempFile); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从嵌入资源中获取文本内容。
|
||||
/// 用于读取 Ribbon XML 定义文件。
|
||||
/// </summary>
|
||||
/// <param name="resourceName">资源名称</param>
|
||||
/// <returns>资源文本内容</returns>
|
||||
private static string GetResourceText(string resourceName)
|
||||
{
|
||||
Assembly asm = Assembly.GetExecutingAssembly();
|
||||
@@ -951,4 +1009,4 @@ namespace WordAddIn
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,36 +11,55 @@ using WordAddIn.Models;
|
||||
|
||||
namespace WordAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// AI 服务核心类。
|
||||
/// 职责:
|
||||
/// 1. 封装与后端大模型 API (如 Gemini, OpenAI) 的 HTTP 通信。
|
||||
/// 2. 构造 System Prompt 和 User Prompt,处理上下文注入。
|
||||
/// 3. 解析 API 返回的 JSON 数据,并处理异常。
|
||||
/// 4. 提供具体的业务接口:扩写、润色、升华、大纲生成、结构分析、配图生成。
|
||||
/// </summary>
|
||||
public class AiService
|
||||
{
|
||||
// 兔子平台配置
|
||||
// 兔子平台配置 (兼容 OpenAI 接口格式)
|
||||
private const string ApiKey = "sk-FCwlaMANKdSlXlY7HkzZncSp0N5gecfpQdk0iR059Hfk4dQ1";
|
||||
private const string BaseUrl = "https://api.tu-zi.com/v1";
|
||||
|
||||
// 模型配置
|
||||
private const string TextModel = "gemini-3-flash-preview";
|
||||
private const string ImageModel = "gemini-3-pro-image-preview";
|
||||
private const string TextModel = "gemini-3-flash-preview"; // 默认文本模型,速度快,成本低
|
||||
private const string ImageModel = "gemini-3-pro-image-preview"; // 默认生图模型,支持高质量绘图
|
||||
|
||||
private static readonly HttpClient _httpClient;
|
||||
|
||||
static AiService()
|
||||
{
|
||||
// 必须强制启用 TLS 1.2,否则无法连接现代 HTTPS 站点
|
||||
// 关键设置:必须强制启用 TLS 1.2,否则在 .NET Framework 4.7.2 下无法连接现代 HTTPS 站点
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
|
||||
_httpClient = new HttpClient();
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(60);
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(60); // 设置 60 秒超时,防止长文本生成时连接断开
|
||||
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {ApiKey}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 智能扩写:将简短的文本扩充为细节丰富、逻辑通顺的段落。
|
||||
/// </summary>
|
||||
/// <param name="originalText">需要扩写的原文</param>
|
||||
/// <param name="customInstruction">用户自定义的扩写要求(如“更幽默一点”、“强调数据支持”等),可选</param>
|
||||
/// <param name="documentContext">文档全文上下文(摘要、风格等),用于保持文风一致,可选</param>
|
||||
/// <returns>扩写后的纯文本内容</returns>
|
||||
public async Task<string> ExpandText(string originalText, string customInstruction = null, string documentContext = null)
|
||||
{
|
||||
// 构造 User 指令
|
||||
string instruction = string.IsNullOrWhiteSpace(customInstruction)
|
||||
? "请将以下内容扩写成一段正式、通顺的段落,保持原意但增加细节"
|
||||
: customInstruction;
|
||||
|
||||
// 构造 System Prompt
|
||||
// 强制要求不使用 Markdown,方便直接插入 Word
|
||||
string systemPrompt = "你是一个专业的文档编辑助手。请直接输出扩写后的纯文本内容,不要使用任何Markdown标记(如#、**、- 等)。直接使用自然段落。";
|
||||
|
||||
// 注入文档上下文(如果有)
|
||||
if (!string.IsNullOrWhiteSpace(documentContext))
|
||||
{
|
||||
systemPrompt += $"\n\n【全文背景与排版规则】:你已通读全文,以下是文档的整体上下文信息和排版规则,请在扩写时保持与全文主题和风格的一致性,并参考排版规则生成对应的层级结构:\n{documentContext}";
|
||||
@@ -55,6 +74,12 @@ namespace WordAddIn
|
||||
return await GetChatCompletion(messages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 润色加工:优化文本的语言表达,提升流畅度和专业性。
|
||||
/// </summary>
|
||||
/// <param name="originalText">原文</param>
|
||||
/// <param name="documentContext">文档全文上下文</param>
|
||||
/// <returns>润色后的文本</returns>
|
||||
public async Task<string> PolishText(string originalText, string documentContext = null)
|
||||
{
|
||||
string systemPrompt = "你是一个专业的文字编辑和润色专家。请对用户提供的文本进行深度润色和梳理。目标是:1. 提升语言的流畅度和专业性;2. 修正语法错误和用词不当;3. 优化逻辑结构,使表达更清晰;4. 保持原意不变。请直接输出润色后的内容,不要包含任何解释或客套话。请直接输出纯文本,不要使用任何Markdown标记。";
|
||||
@@ -73,6 +98,12 @@ namespace WordAddIn
|
||||
return await GetChatCompletion(messages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 深度升华:挖掘文本思想深度,提升价值和高度。
|
||||
/// </summary>
|
||||
/// <param name="originalText">原文</param>
|
||||
/// <param name="documentContext">文档全文上下文</param>
|
||||
/// <returns>升华后的文本</returns>
|
||||
public async Task<string> ElevateText(string originalText, string documentContext = null)
|
||||
{
|
||||
string systemPrompt = "你是一个资深的领域专家和思想家。用户提供了一段文本,请你根据自己的广博知识和深刻理解,对其进行深度升华。目标是:1. 挖掘深度:透过现象看本质,补充更有见地的分析;2. 拓展广度:引入相关的行业背景、前沿趋势或跨学科视角;3. 提升价值:将普通的描述提升为具有指导意义或启发性的洞见;4. 高于原文:不要局限于原文的字面意思,要大胆进行思想层面的拔高。请直接输出升华后的纯文本内容,保持逻辑连贯,不要使用任何Markdown标记。";
|
||||
@@ -91,6 +122,14 @@ namespace WordAddIn
|
||||
return await GetChatCompletion(messages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 素材写作:根据参考资料生成新的内容。
|
||||
/// </summary>
|
||||
/// <param name="originalText">目标文本/上下文</param>
|
||||
/// <param name="referenceContent">参考资料内容</param>
|
||||
/// <param name="wordLimit">字数限制</param>
|
||||
/// <param name="documentContext">文档全文上下文</param>
|
||||
/// <returns>生成的内容</returns>
|
||||
public async Task<string> GenerateWithReference(string originalText, string referenceContent, int wordLimit, string documentContext = null)
|
||||
{
|
||||
string systemPrompt = "你是一个专业的文档写作助手。请根据提供的参考资料,撰写或重写目标文本。请直接输出纯文本内容,不要使用任何Markdown标记。";
|
||||
@@ -131,6 +170,16 @@ namespace WordAddIn
|
||||
return await GetChatCompletion(messages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成大纲:根据主题生成结构化的 JSON 大纲。
|
||||
/// 功能描述:
|
||||
/// 1. 构造 Prompt,要求 AI 返回特定格式的 JSON 数组 (OutlineItem)。
|
||||
/// 2. 调用 AI 接口。
|
||||
/// 3. 清理 AI 可能返回的 Markdown 代码块标记 (```json ... ```)。
|
||||
/// 4. 反序列化 JSON 为对象列表。
|
||||
/// </summary>
|
||||
/// <param name="topic">大纲主题或简要描述</param>
|
||||
/// <returns>大纲项列表 (包含标题和层级)</returns>
|
||||
public async Task<List<OutlineItem>> GenerateOutline(string topic)
|
||||
{
|
||||
string systemPrompt = @"你是一个专业的文档大纲生成助手。请根据用户提供的主题或描述,生成一个结构化的文档大纲。
|
||||
@@ -153,6 +202,7 @@ namespace WordAddIn
|
||||
string jsonResponse = await GetChatCompletion(messages);
|
||||
|
||||
// 清理可能存在的 Markdown 代码块标记
|
||||
// 某些模型即使被要求不返回 Markdown,仍可能习惯性地加上 ```json
|
||||
jsonResponse = jsonResponse.Replace("```json", "").Replace("```", "").Trim();
|
||||
|
||||
try
|
||||
@@ -162,10 +212,21 @@ namespace WordAddIn
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("JSON Parsing Failed: " + ex.Message);
|
||||
// 这里可以考虑返回一个空列表,或者抛出异常让上层处理
|
||||
// 目前选择返回空列表,由 UI 层提示失败
|
||||
return new List<OutlineItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结构分析:分析文档文本,提取标题及其层级结构。
|
||||
/// 功能描述:
|
||||
/// 1. 发送文档内容给 AI。
|
||||
/// 2. AI 识别出所有的标题行及其层级 (1-5)。
|
||||
/// 3. 返回 JSON 格式的标题列表,供格式美化功能使用。
|
||||
/// </summary>
|
||||
/// <param name="text">文档全文内容 (或截断后的部分内容)</param>
|
||||
/// <returns>标题信息列表 (包含标题文本和层级)</returns>
|
||||
public async Task<List<HeadingInfo>> AnalyzeStructure(string text)
|
||||
{
|
||||
string systemPrompt = @"你是一个文档格式专家。请分析用户提供的文档内容,识别出所有的标题及其对应的层级(1级、2级、3级...)。
|
||||
@@ -202,6 +263,11 @@ namespace WordAddIn
|
||||
public int level { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通读全文:分析文档全文,生成摘要和风格画像。
|
||||
/// </summary>
|
||||
/// <param name="fullText">全文内容</param>
|
||||
/// <returns>文档画像描述</returns>
|
||||
public async Task<string> AnalyzeDocument(string fullText)
|
||||
{
|
||||
var messages = new List<Message>
|
||||
@@ -213,6 +279,9 @@ namespace WordAddIn
|
||||
return await GetChatCompletion(messages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化文本(保留接口,目前逻辑主要在 AnalyzeStructure 中实现)。
|
||||
/// </summary>
|
||||
public async Task<string> FormatText(string originalText)
|
||||
{
|
||||
var messages = new List<Message>
|
||||
@@ -224,19 +293,30 @@ namespace WordAddIn
|
||||
return await GetChatCompletion(messages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成图片 URL。
|
||||
/// 流程:
|
||||
/// 1. 优化提示词:将用户简单的中文描述转换为详细的英文绘图 Prompt。
|
||||
/// 2. 调用生图 API (如 DALL-E 3 或 Gemini Image)。
|
||||
/// 3. 解析 API 响应,提取图片 URL。
|
||||
/// </summary>
|
||||
/// <param name="prompt">用户的原始描述</param>
|
||||
/// <param name="style">图片风格 (如"水墨画", "赛博朋克"等)</param>
|
||||
/// <returns>生成的图片 URL (有效期通常较短)</returns>
|
||||
public async Task<string> GenerateImageUrl(string prompt, string style = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 优化 Prompt:将用户输入转换为安全的英文绘图 Prompt,并注入风格
|
||||
// 这一步对于生成高质量图片至关重要,因为绘图模型对英文 Prompt 的理解通常更好
|
||||
string optimizedPrompt = await OptimizePromptForImage(prompt, style);
|
||||
|
||||
// 使用匿名对象以获得最大灵活性
|
||||
// 使用匿名对象构造请求体,以获得最大灵活性
|
||||
var requestBody = new
|
||||
{
|
||||
prompt = optimizedPrompt,
|
||||
model = ImageModel,
|
||||
size = "1024x1024",
|
||||
size = "1024x1024", // 标准分辨率
|
||||
response_format = "url" // 显式请求 URL 格式
|
||||
};
|
||||
|
||||
@@ -251,10 +331,10 @@ namespace WordAddIn
|
||||
throw new Exception($"API Error ({response.StatusCode}): {responseString}");
|
||||
}
|
||||
|
||||
// 使用 JObject 动态解析
|
||||
// 使用 JObject 动态解析响应,以兼容不同厂商的 API 格式差异
|
||||
var jsonResponse = JObject.Parse(responseString);
|
||||
|
||||
// 1. 尝试解析标准格式: data[0].url
|
||||
// 1. 尝试解析 OpenAI 标准格式: data[0].url
|
||||
var data = jsonResponse["data"];
|
||||
if (data != null)
|
||||
{
|
||||
@@ -268,8 +348,6 @@ namespace WordAddIn
|
||||
if (firstItem["revised_prompt"] != null && firstItem["revised_prompt"].ToString() == "NO_IMAGE")
|
||||
{
|
||||
// 尝试从其他字段找,或者这就是个错误
|
||||
// 如果是 NO_IMAGE,通常意味着内容安全拦截或生成失败
|
||||
// 但这里我们先假设它可能在别的字段
|
||||
}
|
||||
|
||||
// 某些 API 可能直接在数组里放 URL 字符串
|
||||
@@ -282,7 +360,7 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 尝试解析根节点 url
|
||||
// 3. 尝试解析根节点 url (部分非标准 API)
|
||||
if (jsonResponse["url"] != null)
|
||||
{
|
||||
return jsonResponse["url"].ToString();
|
||||
@@ -296,6 +374,10 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化图片生成提示词。
|
||||
/// 将用户的中文描述翻译成英文,并加入风格指令。
|
||||
/// </summary>
|
||||
private async Task<string> OptimizePromptForImage(string originalPrompt, string style = null)
|
||||
{
|
||||
string styleInstruction = string.IsNullOrEmpty(style) ? "" : $"6. 图片风格必须严格遵循:{style}。";
|
||||
@@ -309,6 +391,9 @@ namespace WordAddIn
|
||||
return await GetChatCompletion(messages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通用聊天补全请求方法。
|
||||
/// </summary>
|
||||
private async Task<string> GetChatCompletion(List<Message> messages)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -5,8 +5,18 @@ using System.Windows.Forms;
|
||||
|
||||
namespace WordAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 智能扩写设置窗体。
|
||||
/// 功能描述:
|
||||
/// 1. 提供多种预设的扩写模板(如"商务正式"、"细腻情感"等)。
|
||||
/// 2. 允许用户修改预设模板或输入完全自定义的扩写要求。
|
||||
/// 3. 返回用户的最终选择 (SelectedPrompt) 给调用方。
|
||||
/// </summary>
|
||||
public class ExpandOptionsForm : Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户最终确认的扩写要求。
|
||||
/// </summary>
|
||||
public string SelectedPrompt { get; private set; }
|
||||
|
||||
private ComboBox cmbTemplates;
|
||||
@@ -32,6 +42,9 @@ namespace WordAddIn
|
||||
InitializeTemplates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化窗体组件。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lblTemplate = new System.Windows.Forms.Label();
|
||||
@@ -124,6 +137,9 @@ namespace WordAddIn
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化模板下拉列表。
|
||||
/// </summary>
|
||||
private void InitializeTemplates()
|
||||
{
|
||||
foreach (var key in _templates.Keys)
|
||||
@@ -136,6 +152,10 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模板选择改变事件。
|
||||
/// 当用户选择不同模板时,自动填充对应的 Prompt 到文本框中。
|
||||
/// </summary>
|
||||
private void CmbTemplates_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (cmbTemplates.SelectedItem != null && _templates.ContainsKey(cmbTemplates.SelectedItem.ToString()))
|
||||
@@ -144,6 +164,10 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确定按钮点击事件。
|
||||
/// 验证输入有效性,并保存结果。
|
||||
/// </summary>
|
||||
private void BtnOk_Click(object sender, EventArgs e)
|
||||
{
|
||||
SelectedPrompt = txtCustomPrompt.Text.Trim();
|
||||
|
||||
@@ -6,9 +6,23 @@ using WordAddIn.Properties;
|
||||
|
||||
namespace WordAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成配图设置窗体。
|
||||
/// 功能描述:
|
||||
/// 1. 允许用户选择图片风格(如写实、卡通、3D渲染等)。
|
||||
/// 2. 允许用户输入额外的描述(如颜色、背景要求等)。
|
||||
/// 3. 支持保存上次选择的风格,提升用户体验。
|
||||
/// </summary>
|
||||
public class ImageOptionsForm : Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户选择的图片风格。
|
||||
/// </summary>
|
||||
public string SelectedStyle { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户输入的补充描述。
|
||||
/// </summary>
|
||||
public string AdditionalPrompt { get; private set; }
|
||||
|
||||
private ComboBox cmbStyles;
|
||||
@@ -36,6 +50,9 @@ namespace WordAddIn
|
||||
InitializeStyles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化窗体组件。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lblStyle = new System.Windows.Forms.Label();
|
||||
@@ -127,6 +144,9 @@ namespace WordAddIn
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化风格列表,并尝试加载上次保存的风格。
|
||||
/// </summary>
|
||||
private void InitializeStyles()
|
||||
{
|
||||
cmbStyles.Items.AddRange(_styles);
|
||||
@@ -143,6 +163,10 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确定按钮点击事件。
|
||||
/// 获取用户选择,并保存风格到 Settings。
|
||||
/// </summary>
|
||||
private void BtnOk_Click(object sender, EventArgs e)
|
||||
{
|
||||
SelectedStyle = cmbStyles.SelectedItem.ToString();
|
||||
|
||||
@@ -5,6 +5,13 @@ using System.Windows.Forms;
|
||||
|
||||
namespace WordAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载动画窗体。
|
||||
/// 功能描述:
|
||||
/// 1. 在执行耗时操作(如 AI 生成)时显示,阻断用户界面。
|
||||
/// 2. 使用 GDI+ 自定义绘制旋转动画 (Spinner)。
|
||||
/// 3. 支持跨线程安全关闭。
|
||||
/// </summary>
|
||||
public class LoadingForm : Form
|
||||
{
|
||||
private System.Windows.Forms.Label lblMessage;
|
||||
@@ -18,6 +25,10 @@ namespace WordAddIn
|
||||
private readonly Color _backgroundColor = Color.White;
|
||||
private readonly Color _borderColor = Color.FromArgb(100, 100, 100);
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数。
|
||||
/// </summary>
|
||||
/// <param name="message">加载时显示的提示文字</param>
|
||||
public LoadingForm(string message = "AI 正在思考中...")
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -28,6 +39,9 @@ namespace WordAddIn
|
||||
this.UpdateStyles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化窗体组件。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
@@ -71,12 +85,18 @@ namespace WordAddIn
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动画定时器触发事件。更新旋转角度并重绘。
|
||||
/// </summary>
|
||||
private void AnimationTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
_spinnerAngle = (_spinnerAngle + 10) % 360;
|
||||
this.Invalidate(); // 触发重绘
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写绘制事件,自定义绘制加载动画。
|
||||
/// </summary>
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
base.OnPaint(e);
|
||||
@@ -127,7 +147,10 @@ namespace WordAddIn
|
||||
g.ResetTransform();
|
||||
}
|
||||
|
||||
// 允许在非 UI 线程关闭窗体
|
||||
/// <summary>
|
||||
/// 线程安全的关闭方法。
|
||||
/// 允许从非 UI 线程调用。
|
||||
/// </summary>
|
||||
public void CloseSafe()
|
||||
{
|
||||
if (this.IsDisposed) return;
|
||||
@@ -143,7 +166,9 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
// 允许拖动窗体
|
||||
/// <summary>
|
||||
/// 支持鼠标拖动无边框窗体。
|
||||
/// </summary>
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
|
||||
@@ -4,6 +4,13 @@ using System.Windows.Forms;
|
||||
|
||||
namespace WordAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户登录窗体。
|
||||
/// 功能描述:
|
||||
/// 1. 模拟用户登录验证过程。
|
||||
/// 2. 提供用户名和密码输入框。
|
||||
/// 3. 目前使用硬编码的凭证 (huanghai / 12345678) 进行验证,实际生产中应连接认证服务。
|
||||
/// </summary>
|
||||
public class LoginForm : Form
|
||||
{
|
||||
private TextBox txtUsername;
|
||||
@@ -19,6 +26,9 @@ namespace WordAddIn
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化窗体组件。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.lblTitle = new System.Windows.Forms.Label();
|
||||
@@ -135,6 +145,10 @@ namespace WordAddIn
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登录按钮点击事件。
|
||||
/// 验证用户名和密码。
|
||||
/// </summary>
|
||||
private void BtnLogin_Click(object sender, EventArgs e)
|
||||
{
|
||||
string user = txtUsername.Text.Trim();
|
||||
|
||||
@@ -3,70 +3,208 @@ using System.Collections.Generic;
|
||||
|
||||
namespace WordAddIn.Models
|
||||
{
|
||||
// Chat Completion Models
|
||||
// ==========================================
|
||||
// Chat Completion API 模型定义
|
||||
// 用于与 OpenAI / Gemini 兼容的聊天接口交互
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 聊天补全请求对象
|
||||
/// </summary>
|
||||
public class ChatCompletionRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型名称 (e.g., "gpt-3.5-turbo", "gemini-3-flash")
|
||||
/// </summary>
|
||||
public string model { get; set; } = "gpt-3.5-turbo";
|
||||
|
||||
/// <summary>
|
||||
/// 消息列表,包含 system, user, assistant 等角色的消息
|
||||
/// </summary>
|
||||
public List<Message> messages { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 温度参数,控制生成的随机性 (0.0 - 1.0)
|
||||
/// 较高值使输出更随机,较低值使输出更确定
|
||||
/// </summary>
|
||||
public double temperature { get; set; } = 0.7;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 聊天消息对象
|
||||
/// </summary>
|
||||
public class Message
|
||||
{
|
||||
/// <summary>
|
||||
/// 角色: system, user, assistant
|
||||
/// </summary>
|
||||
public string role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
public string content { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 聊天补全响应对象
|
||||
/// </summary>
|
||||
public class ChatCompletionResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 响应 ID
|
||||
/// </summary>
|
||||
public string id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成的选择列表 (通常只取第一个)
|
||||
/// </summary>
|
||||
public List<Choice> choices { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成结果选项
|
||||
/// </summary>
|
||||
public class Choice
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成的消息内容
|
||||
/// </summary>
|
||||
public Message message { get; set; }
|
||||
}
|
||||
|
||||
// Image Generation Models
|
||||
// ==========================================
|
||||
// Image Generation API 模型定义
|
||||
// 用于与 DALL-E / Gemini Image 接口交互
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 图片生成请求对象
|
||||
/// </summary>
|
||||
public class ImageGenerationRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型名称
|
||||
/// </summary>
|
||||
public string model { get; set; } = "dall-e-3";
|
||||
|
||||
/// <summary>
|
||||
/// 绘图提示词
|
||||
/// </summary>
|
||||
public string prompt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生成数量 (DALL-E 3 通常限制为 1)
|
||||
/// </summary>
|
||||
public int n { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 图片尺寸 (e.g., "1024x1024")
|
||||
/// </summary>
|
||||
public string size { get; set; } = "1024x1024";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图片生成响应对象
|
||||
/// </summary>
|
||||
public class ImageGenerationResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成的数据列表
|
||||
/// </summary>
|
||||
public List<ImageItem> data { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图片数据项
|
||||
/// </summary>
|
||||
public class ImageItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 图片 URL
|
||||
/// </summary>
|
||||
public string url { get; set; }
|
||||
}
|
||||
|
||||
// Outline Models
|
||||
// ==========================================
|
||||
// 业务逻辑模型定义
|
||||
// 用于大纲生成和样式配置
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// 大纲条目
|
||||
/// 用于接收 AI 生成的结构化大纲数据
|
||||
/// </summary>
|
||||
public class OutlineItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 标题文本
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public int Level { get; set; } // 0=MainTitle, 1=H1, 2=H2, 3=H3
|
||||
|
||||
/// <summary>
|
||||
/// 标题层级
|
||||
/// 0: 主标题
|
||||
/// 1: 一级标题
|
||||
/// 2: 二级标题
|
||||
/// 3: 三级标题
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 大纲样式配置集合
|
||||
/// 存储各级标题的样式设置
|
||||
/// </summary>
|
||||
public class OutlineStyleConfig
|
||||
{
|
||||
public TextStyle MainTitle { get; set; } = new TextStyle { FontSize = 22, IsBold = true, FontName = "微软雅黑", Alignment = 1 }; // Center
|
||||
/// <summary>
|
||||
/// 主标题样式
|
||||
/// </summary>
|
||||
public TextStyle MainTitle { get; set; } = new TextStyle { FontSize = 22, IsBold = true, FontName = "微软雅黑", Alignment = 1 }; // 默认为居中
|
||||
|
||||
/// <summary>
|
||||
/// 一级标题样式
|
||||
/// </summary>
|
||||
public TextStyle Heading1 { get; set; } = new TextStyle { FontSize = 16, IsBold = true, FontName = "黑体" };
|
||||
|
||||
/// <summary>
|
||||
/// 二级标题样式
|
||||
/// </summary>
|
||||
public TextStyle Heading2 { get; set; } = new TextStyle { FontSize = 14, IsBold = true, FontName = "黑体" };
|
||||
|
||||
/// <summary>
|
||||
/// 三级标题样式
|
||||
/// </summary>
|
||||
public TextStyle Heading3 { get; set; } = new TextStyle { FontSize = 12, IsBold = true, FontName = "宋体" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文本样式定义
|
||||
/// </summary>
|
||||
public class TextStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// 字体名称
|
||||
/// </summary>
|
||||
public string FontName { get; set; } = "宋体";
|
||||
|
||||
/// <summary>
|
||||
/// 字号 (磅值)
|
||||
/// </summary>
|
||||
public float FontSize { get; set; } = 12;
|
||||
|
||||
/// <summary>
|
||||
/// 是否加粗
|
||||
/// </summary>
|
||||
public bool IsBold { get; set; } = false;
|
||||
public int Alignment { get; set; } = 0; // 0=Left, 1=Center, 2=Right
|
||||
|
||||
/// <summary>
|
||||
/// 对齐方式
|
||||
/// 0: 左对齐
|
||||
/// 1: 居中
|
||||
/// 2: 右对齐
|
||||
/// </summary>
|
||||
public int Alignment { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,13 @@ using System.IO;
|
||||
|
||||
namespace WordAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 素材写作(参考生成)配置窗体。
|
||||
/// 功能描述:
|
||||
/// 1. 允许用户提供参考素材,用于辅助 AI 生成内容。
|
||||
/// 2. 支持两种输入模式:上传文件 (Word/Txt) 或 直接输入文本。
|
||||
/// 3. 允许用户设置生成内容的字数上限。
|
||||
/// </summary>
|
||||
public class ReferenceGeneratorForm : Form
|
||||
{
|
||||
private TabControl tabControl;
|
||||
@@ -20,9 +27,24 @@ namespace WordAddIn
|
||||
private Label lblWordCount;
|
||||
private Label lblWordUnit;
|
||||
|
||||
/// <summary>
|
||||
/// 获取直接输入的参考文本内容。
|
||||
/// </summary>
|
||||
public string ReferenceContent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户设置的字数上限。
|
||||
/// </summary>
|
||||
public int WordLimit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 指示是否选择了文件模式。
|
||||
/// </summary>
|
||||
public bool IsFileMode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户选择的文件路径。
|
||||
/// </summary>
|
||||
public string SelectedFilePath { get; private set; }
|
||||
|
||||
public ReferenceGeneratorForm()
|
||||
@@ -30,6 +52,9 @@ namespace WordAddIn
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化窗体组件。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tabControl = new System.Windows.Forms.TabControl();
|
||||
@@ -235,6 +260,10 @@ namespace WordAddIn
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 浏览按钮点击事件。
|
||||
/// 打开文件选择对话框,选择参考文档。
|
||||
/// </summary>
|
||||
private void BtnBrowse_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (OpenFileDialog ofd = new OpenFileDialog())
|
||||
@@ -248,6 +277,10 @@ namespace WordAddIn
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成按钮点击事件。
|
||||
/// 验证输入有效性,并保存用户的选择。
|
||||
/// </summary>
|
||||
private void BtnGenerate_Click(object sender, EventArgs e)
|
||||
{
|
||||
WordLimit = (int)numWordCount.Value;
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
namespace WordAddIn
|
||||
namespace WordAddIn
|
||||
{
|
||||
public partial class ThisAddIn
|
||||
{
|
||||
/// <summary>
|
||||
/// 覆盖 CreateRibbonExtensibilityObject 方法。
|
||||
/// 返回我们自定义的 Ribbon 实例 (AiRibbon)。
|
||||
/// VSTO 在加载时会调用此方法来获取 Ribbon XML 和回调处理程序。
|
||||
/// </summary>
|
||||
/// <returns>IRibbonExtensibility 接口实现</returns>
|
||||
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
|
||||
{
|
||||
return new AiRibbon();
|
||||
@@ -9,10 +15,12 @@
|
||||
|
||||
private void ThisAddIn_Startup(object sender, System.EventArgs e)
|
||||
{
|
||||
// 在此添加初始化代码
|
||||
}
|
||||
|
||||
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
|
||||
{
|
||||
// 在此添加清理代码
|
||||
}
|
||||
|
||||
#region VSTO 生成的代码
|
||||
|
||||
Binary file not shown.
@@ -166,14 +166,14 @@
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="WordAddIn.dll" size="92672">
|
||||
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="WordAddIn.dll" size="96256">
|
||||
<assemblyIdentity name="WordAddIn" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
|
||||
<hash>
|
||||
<dsig:Transforms>
|
||||
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
|
||||
</dsig:Transforms>
|
||||
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
|
||||
<dsig:DigestValue>kaNoiBD64kA337KwwCyQswknaKN/Jm2xVBMG2u/zfjM=</dsig:DigestValue>
|
||||
<dsig:DigestValue>57KGd7UXAGzFPWql99dRDRDZXoe2StLuM7Uyd52MAcU=</dsig:DigestValue>
|
||||
</hash>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
@@ -200,4 +200,4 @@
|
||||
</vstov4:customizations>
|
||||
</vstav3:application>
|
||||
</vstav3:addIn>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>gCkchTa6A61vV45e2JsTggjhXU+MDFhh5zWDEljDxY4=</DigestValue></Reference></SignedInfo><SignatureValue>KT7wTPexe3IeF5T7gn7yChiy5gXySA8+kakdFMgJMYZy1G0xI5ojr1bKiB4m9l0kPeNjDMhD7NSSpUOhyyT57CsjriDL2JN6pzRIItHEfIyUz5kwUkQoPDpebaVMWeXxIy0fbrT691Z7c1AC2BSG3uVwq487YB1CqqzufpclDqo=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="8ec5c358128335e761580c8c4f5de10882139bd85e8e576fad03ba36851c2980" Description="" Url=""><as:assemblyIdentity name="WordAddIn.dll" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>rIvGDs7aJWd5U9eWWhzLMnDB7NI0COaBGFP9BeGNrj8=</DigestValue></Reference></SignedInfo><SignatureValue>ExruAcyG+I6xR7ACNNi1O9NI+7HWanTyJDMNa4Dh6G61sVw1AOOkfrrKJlGEy43t/XpubZu90wviQu7/Fkf/02rrYxUjhmP6IyCiMQLuTkDPm01+ExgWzIxjufW0lOYEsr5KvXJsLMKKKx4qAYoz+hd3tIzB01lwKqvSwkXP1sY=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>Q59Lub3/7zQPl9a5u6WlazdugvzYLbuMA2ScUeG2Kgc=</DigestValue></Reference></SignedInfo><SignatureValue>e+fTQJmSYA2Ba8atHBYYQHQ3yumSSKoHkFWQhSnBCeAJ3fJdgAe1BT50EjnkKWP6bgL7txi2fDWiqXH7PomA+4D+t9Px5E2mRjSn6TocihjON9kFjatapE5bYrXX4rxZmsa7baDmtRolKjErgeei1cgkLG+00xgJkUDvKwgLaZM=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="072ab6e1519c64038cbb2dd8fc826e376ba5a5bbb9d6970f34efffbdb94b9f43" Description="" Url=""><as:assemblyIdentity name="WordAddIn.dll" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>BowY9B2/AdRMLaAmKgcs3z/FtyzUeliTzB6MSGgWv2w=</DigestValue></Reference></SignedInfo><SignatureValue>m1b3Quuv79UlTgmBro71Cl0hJmhy+RrFxAwWuFPtCzZmP7iI7KNCfbMMwSWufjzNYW/VF9ShKMk5nWLC1Q7iujoTWIYyUuiIDawYYrTyK4gO3QgQjE06b5DoluCNz4gENwYt2WJvNGRewiLCIEnDZhpAm/BaMJkSbYAkD22ASIY=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
Binary file not shown.
@@ -14,8 +14,8 @@
|
||||
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
|
||||
</dsig:Transforms>
|
||||
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
|
||||
<dsig:DigestValue>PPmVr+YsqgTyziCPQl2d+8CBQOBV0DzcZLxUnUr5dGA=</dsig:DigestValue>
|
||||
<dsig:DigestValue>kQ6lvB1J+RPqWmN3TV1FKCJZwSRY6p4lkcGyY6yVSYM=</dsig:DigestValue>
|
||||
</hash>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>gmWu531mSYE6e+478tWijNz+HJGL6Ub2q/oN99Yoq8g=</DigestValue></Reference></SignedInfo><SignatureValue>S6nctbanxh4cDuBGhiBntmVFtWyc/iVYn8rSFOXQEh7EgUG7v9FMK2qtYi6up36d5NWKvuV+kzKs5uOuUIg1P/hBWLVaRLzQSUQC9vekWmdUcrYjuQGrN7eqfJSK/H9QA1W9pbJ8KEDHQB82v9YQGVLhCn5Q3AxGlAWmaKQETZM=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="c8ab28d6f70dfaabf646e98b911cfedc8ca2d5f23bee7b3a8149667de7ae6582" Description="" Url=""><as:assemblyIdentity name="WordAddIn.vsto" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>BUiNKpXwHd6U/7xnyou3wQDJxj8V+19+IoADYwPgppY=</DigestValue></Reference></SignedInfo><SignatureValue>a2Wc9bjZ7YYsiAqAYwyYkYVJWXxl68YxMoNuB4DYMbo9uBiuMK+fMHN4ko9mqesTW3u7cxiI+HCZgPrEfG7yJdHo67+qdoXFThYYCvD0kCRLPM2s44twfV4pFtiGn5NZtoltrmevUR6pS9VFUAiYtKTygKHsU7CjXfa3UfTUV3c=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>nO4LOJBDcGxL8l8R9b9+yIX5TXJ4Ym9VOMcF/Sry+qw=</DigestValue></Reference></SignedInfo><SignatureValue>ZXK46Htr5iGt1kgI+VOKE9t9CPf3Xfkp16PHJxaK38qkTVXu/oylz4UUYGk/Tf1JuEkuQq06zN6CYCma0rwL4Gyoo/5n09Xzg3zmriPEIS2cs8BPbXwH90FbQ0HDaPVM+dJ9A8c+KTH9h6zNm8aj/HQdBRamLUv1D2PBxk7NE1s=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="acfaf22afd05c738556f6278724df985c87ebff5115ff24b6c704390380bee9c" Description="" Url=""><as:assemblyIdentity name="WordAddIn.vsto" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>9TyNXNxi0FyDrOMgoJ3zDQERhOeW01TflRltAh+X1k8=</DigestValue></Reference></SignedInfo><SignatureValue>DHwRL4z0MQqHXYXDBnMyS39H5LqOPVJmaNB+zmQ+YoMUdVQojZBKuqSvpw3WvvWLhmP5dAH92Q94xrTd0gGggHPmL2r5/puOuDzYIhrvSXhE5dVoUwv8GW5Lem+FjDcAYn6TFic2CEkvwOE9FKEfT92ijcaa4K8AEWPm8X64EKg=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user