diff --git a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db
index 4bdc55a..b3f5ffa 100644
Binary files a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db and b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/CodeChunks.db differ
diff --git a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db
index f051733..6d7dd6c 100644
Binary files a/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db and b/WordAddIn/.vs/WordAddIn/CopilotIndices/17.14.1577.30250/SemanticSymbols.db differ
diff --git a/WordAddIn/.vs/WordAddIn/v17/.suo b/WordAddIn/.vs/WordAddIn/v17/.suo
index c243eff..3b19fbd 100644
Binary files a/WordAddIn/.vs/WordAddIn/v17/.suo and b/WordAddIn/.vs/WordAddIn/v17/.suo differ
diff --git a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json
index 131493c..196c58c 100644
--- a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json
+++ b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.backup.json
@@ -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"
}
]
}
diff --git a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json
index 131493c..196c58c 100644
--- a/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json
+++ b/WordAddIn/.vs/WordAddIn/v17/DocumentLayout.json
@@ -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"
}
]
}
diff --git a/WordAddIn/WordAddIn/AiRibbon.cs b/WordAddIn/WordAddIn/AiRibbon.cs
index 1c273b0..dab926e 100644
--- a/WordAddIn/WordAddIn/AiRibbon.cs
+++ b/WordAddIn/WordAddIn/AiRibbon.cs
@@ -16,21 +16,44 @@ using Newtonsoft.Json;
namespace WordAddIn
{
+ ///
+ /// Word 插件的主入口类,负责处理 Ribbon 功能区的交互逻辑。
+ /// 实现了 IRibbonExtensibility 接口,用于自定义 Word 的功能区界面。
+ /// 包含了所有 Ribbon 按钮的点击事件处理方法。
+ ///
[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;
+ ///
+ /// 默认构造函数
+ ///
public AiRibbon()
{
}
#region IRibbonExtensibility 成员
+ ///
+ /// 加载自定义 Ribbon XML 界面定义。
+ /// 该方法由 VSTO 运行时调用。
+ ///
+ /// Ribbon ID
+ /// 包含 Ribbon 定义的 XML 字符串
public string GetCustomUI(string ribbonID)
{
return GetResourceText("WordAddIn.AiRibbon.xml");
@@ -40,11 +63,22 @@ namespace WordAddIn
#region Ribbon 回调
+ ///
+ /// Ribbon 加载完成时的回调方法。
+ /// 保存 Ribbon UI 实例引用,以便后续可以使用它来刷新 Ribbon 控件的状态。
+ ///
+ /// Ribbon UI 实例
public void Ribbon_Load(Office.IRibbonUI ribbonUI)
{
this.ribbon = ribbonUI;
}
+ ///
+ /// 获取组合的上下文信息,包含全文背景和外部参考资料。
+ /// 将 _documentContext (全文背景) 和 _referenceContext (外部参考) 拼接成一个字符串。
+ /// 用于发送给 AI 以提供更准确的生成结果,确保生成内容与文档上下文和参考资料保持一致。
+ ///
+ /// 组合后的上下文描述字符串
private string GetCombinedContext()
{
StringBuilder sb = new StringBuilder();
@@ -63,13 +97,20 @@ namespace WordAddIn
return sb.ToString();
}
+ ///
+ /// 【补充背景】按钮点击事件处理。
+ /// 允许用户导入外部文档 (txt, doc, docx) 作为背景资料。
+ /// 读取的文件内容将存储在 _referenceContext 中,供后续 AI 生成任务使用。
+ ///
+ /// 触发事件的 Ribbon 控件
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
}
}
+ ///
+ /// 【通读全文】按钮点击事件处理。
+ /// 读取当前文档的全部文本内容,调用 AI 进行深度语义分析。
+ /// 生成的文档画像(包括摘要、风格、术语等)将存储在 _documentContext 中。
+ ///
+ /// 触发事件的 Ribbon 控件
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
}
}
+ ///
+ /// 【润色加工】按钮点击事件处理。
+ /// 功能描述:
+ /// 1. 获取选中文本(或剪贴板内容)。
+ /// 2. 调用 AI 润色接口,优化语言流畅度和专业性。
+ /// 3. 将润色后的文本替换回文档。
+ ///
+ /// 触发事件的 Ribbon 控件
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
}
}
+ ///
+ /// 【深度升华】按钮点击事件处理。
+ /// 功能描述:
+ /// 1. 获取选中文本。
+ /// 2. 调用 AI 升华接口,挖掘思想深度,提升价值。
+ /// 3. 将升华后的文本插入文档。
+ ///
+ /// 触发事件的 Ribbon 控件
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
}
}
+ ///
+ /// 【智能扩写】按钮点击事件处理。
+ /// 功能描述:
+ /// 1. 获取用户选中的文本(或剪贴板内容)。
+ /// 2. 弹出选项窗体,允许用户输入自定义扩写提示词。
+ /// 3. 调用 AI 扩写接口,传入原文、用户提示和全文上下文。
+ /// 4. 将 AI 生成的扩写内容替换原有选区,或插入到光标位置。
+ ///
+ /// 触发事件的 Ribbon 控件
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
}
}
+ ///
+ /// 【素材写作】按钮点击事件处理。
+ /// 功能描述:
+ /// 1. 获取选中文本作为上下文(可选)。
+ /// 2. 弹出素材写作配置窗体,允许用户输入文本素材或上传文档素材。
+ /// 3. 解析上传的文件内容(支持 .txt, .doc, .docx)。
+ /// 4. 调用 AI 接口,根据素材生成新的内容。
+ ///
+ /// 触发事件的 Ribbon 控件
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
}
}
+ ///
+ /// 智能插入辅助方法。
+ /// 使用 TypeText 方法插入文本,以保持当前光标处的格式。
+ /// 如果 TypeText 失败,则回退到直接赋值 Text 属性。
+ ///
+ /// Word 选区对象
+ /// 要插入的文本
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
}
}
+ ///
+ /// 【格式美化】按钮点击事件处理。
+ /// 功能描述:
+ /// 1. 读取当前文档全文。
+ /// 2. 调用 AI 分析文档结构,识别各级标题。
+ /// 3. 遍历文档段落,根据 AI 返回的标题列表,将匹配的段落设置为对应的标题样式 (Heading 1-5)。
+ /// 4. 对非标题的正文段落应用首行缩进。
+ ///
+ /// 触发事件的 Ribbon 控件
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();
- // 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
}
}
+ ///
+ /// 【配图生成】按钮点击事件处理。
+ /// 功能描述:
+ /// 1. 获取用户选中的文字作为绘图描述 (Prompt)。
+ /// 2. 弹出图片风格设置窗体,让用户选择风格(如写实、卡通、水墨等)和补充要求。
+ /// 3. 调用 AI 绘图接口 (DALL-E 3 / Gemini Image),获取生成图片的 URL。
+ /// 4. 下载图片并插入到 Word 文档中。
+ ///
+ /// 触发事件的 Ribbon 控件
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
}
}
+ ///
+ /// 【创建大纲】按钮点击事件处理。
+ /// 功能描述:
+ /// 1. 弹出模态对话框让用户输入大纲主题。
+ /// 2. 调用 AI 服务生成结构化的 JSON 大纲数据。
+ /// 3. 解析 JSON 数据,遍历大纲节点。
+ /// 4. 将大纲内容插入 Word 文档,并根据层级应用预设的字体、字号、对齐方式等样式。
+ ///
+ /// 触发事件的 Ribbon 控件 (由 VSTO 运行时传入)
+ 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 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(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();
+ }
+ }
+
+ ///
+ /// 【大纲配置】按钮点击事件处理。
+ /// 打开大纲样式配置窗口,允许用户自定义各级标题的字体、字号等。
+ ///
+ /// 触发事件的 Ribbon 控件
public void OnOutlineSettingClick(Office.IRibbonControl control)
{
if (!EnsureLoggedIn()) return;
+
using (var form = new OutlineSettingsForm())
{
form.ShowDialog();
}
}
- public async void OnCreateOutlineClick(Office.IRibbonControl control)
+ ///
+ /// 从 URL 插入图片到 Word 文档中。
+ /// 下载图片并保存为临时文件,然后插入到当前光标位置。
+ ///
+ /// 图片的 URL 地址
+ 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(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 帮助器
-
+ ///
+ /// 确保用户已登录。
+ /// 检查 _isLoggedIn 状态,如果未登录则弹出登录窗口。
+ ///
+ /// 如果已登录或登录成功返回 true,否则返回 false
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 { }
- }
- }
- }
-
+ ///
+ /// 从嵌入资源中获取文本内容。
+ /// 用于读取 Ribbon XML 定义文件。
+ ///
+ /// 资源名称
+ /// 资源文本内容
private static string GetResourceText(string resourceName)
{
Assembly asm = Assembly.GetExecutingAssembly();
@@ -951,4 +1009,4 @@ namespace WordAddIn
#endregion
}
-}
+}
\ No newline at end of file
diff --git a/WordAddIn/WordAddIn/AiService.cs b/WordAddIn/WordAddIn/AiService.cs
index ac24edb..75d0a3b 100644
--- a/WordAddIn/WordAddIn/AiService.cs
+++ b/WordAddIn/WordAddIn/AiService.cs
@@ -11,36 +11,55 @@ using WordAddIn.Models;
namespace WordAddIn
{
+ ///
+ /// AI 服务核心类。
+ /// 职责:
+ /// 1. 封装与后端大模型 API (如 Gemini, OpenAI) 的 HTTP 通信。
+ /// 2. 构造 System Prompt 和 User Prompt,处理上下文注入。
+ /// 3. 解析 API 返回的 JSON 数据,并处理异常。
+ /// 4. 提供具体的业务接口:扩写、润色、升华、大纲生成、结构分析、配图生成。
+ ///
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}");
}
+ ///
+ /// 智能扩写:将简短的文本扩充为细节丰富、逻辑通顺的段落。
+ ///
+ /// 需要扩写的原文
+ /// 用户自定义的扩写要求(如“更幽默一点”、“强调数据支持”等),可选
+ /// 文档全文上下文(摘要、风格等),用于保持文风一致,可选
+ /// 扩写后的纯文本内容
public async Task 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);
}
+ ///
+ /// 润色加工:优化文本的语言表达,提升流畅度和专业性。
+ ///
+ /// 原文
+ /// 文档全文上下文
+ /// 润色后的文本
public async Task PolishText(string originalText, string documentContext = null)
{
string systemPrompt = "你是一个专业的文字编辑和润色专家。请对用户提供的文本进行深度润色和梳理。目标是:1. 提升语言的流畅度和专业性;2. 修正语法错误和用词不当;3. 优化逻辑结构,使表达更清晰;4. 保持原意不变。请直接输出润色后的内容,不要包含任何解释或客套话。请直接输出纯文本,不要使用任何Markdown标记。";
@@ -73,6 +98,12 @@ namespace WordAddIn
return await GetChatCompletion(messages);
}
+ ///
+ /// 深度升华:挖掘文本思想深度,提升价值和高度。
+ ///
+ /// 原文
+ /// 文档全文上下文
+ /// 升华后的文本
public async Task ElevateText(string originalText, string documentContext = null)
{
string systemPrompt = "你是一个资深的领域专家和思想家。用户提供了一段文本,请你根据自己的广博知识和深刻理解,对其进行深度升华。目标是:1. 挖掘深度:透过现象看本质,补充更有见地的分析;2. 拓展广度:引入相关的行业背景、前沿趋势或跨学科视角;3. 提升价值:将普通的描述提升为具有指导意义或启发性的洞见;4. 高于原文:不要局限于原文的字面意思,要大胆进行思想层面的拔高。请直接输出升华后的纯文本内容,保持逻辑连贯,不要使用任何Markdown标记。";
@@ -91,6 +122,14 @@ namespace WordAddIn
return await GetChatCompletion(messages);
}
+ ///
+ /// 素材写作:根据参考资料生成新的内容。
+ ///
+ /// 目标文本/上下文
+ /// 参考资料内容
+ /// 字数限制
+ /// 文档全文上下文
+ /// 生成的内容
public async Task GenerateWithReference(string originalText, string referenceContent, int wordLimit, string documentContext = null)
{
string systemPrompt = "你是一个专业的文档写作助手。请根据提供的参考资料,撰写或重写目标文本。请直接输出纯文本内容,不要使用任何Markdown标记。";
@@ -131,6 +170,16 @@ namespace WordAddIn
return await GetChatCompletion(messages);
}
+ ///
+ /// 生成大纲:根据主题生成结构化的 JSON 大纲。
+ /// 功能描述:
+ /// 1. 构造 Prompt,要求 AI 返回特定格式的 JSON 数组 (OutlineItem)。
+ /// 2. 调用 AI 接口。
+ /// 3. 清理 AI 可能返回的 Markdown 代码块标记 (```json ... ```)。
+ /// 4. 反序列化 JSON 为对象列表。
+ ///
+ /// 大纲主题或简要描述
+ /// 大纲项列表 (包含标题和层级)
public async Task> 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();
}
}
+ ///
+ /// 结构分析:分析文档文本,提取标题及其层级结构。
+ /// 功能描述:
+ /// 1. 发送文档内容给 AI。
+ /// 2. AI 识别出所有的标题行及其层级 (1-5)。
+ /// 3. 返回 JSON 格式的标题列表,供格式美化功能使用。
+ ///
+ /// 文档全文内容 (或截断后的部分内容)
+ /// 标题信息列表 (包含标题文本和层级)
public async Task> AnalyzeStructure(string text)
{
string systemPrompt = @"你是一个文档格式专家。请分析用户提供的文档内容,识别出所有的标题及其对应的层级(1级、2级、3级...)。
@@ -202,6 +263,11 @@ namespace WordAddIn
public int level { get; set; }
}
+ ///
+ /// 通读全文:分析文档全文,生成摘要和风格画像。
+ ///
+ /// 全文内容
+ /// 文档画像描述
public async Task AnalyzeDocument(string fullText)
{
var messages = new List
@@ -213,6 +279,9 @@ namespace WordAddIn
return await GetChatCompletion(messages);
}
+ ///
+ /// 格式化文本(保留接口,目前逻辑主要在 AnalyzeStructure 中实现)。
+ ///
public async Task FormatText(string originalText)
{
var messages = new List
@@ -224,19 +293,30 @@ namespace WordAddIn
return await GetChatCompletion(messages);
}
+ ///
+ /// 生成图片 URL。
+ /// 流程:
+ /// 1. 优化提示词:将用户简单的中文描述转换为详细的英文绘图 Prompt。
+ /// 2. 调用生图 API (如 DALL-E 3 或 Gemini Image)。
+ /// 3. 解析 API 响应,提取图片 URL。
+ ///
+ /// 用户的原始描述
+ /// 图片风格 (如"水墨画", "赛博朋克"等)
+ /// 生成的图片 URL (有效期通常较短)
public async Task 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
}
}
+ ///
+ /// 优化图片生成提示词。
+ /// 将用户的中文描述翻译成英文,并加入风格指令。
+ ///
private async Task OptimizePromptForImage(string originalPrompt, string style = null)
{
string styleInstruction = string.IsNullOrEmpty(style) ? "" : $"6. 图片风格必须严格遵循:{style}。";
@@ -309,6 +391,9 @@ namespace WordAddIn
return await GetChatCompletion(messages);
}
+ ///
+ /// 通用聊天补全请求方法。
+ ///
private async Task GetChatCompletion(List messages)
{
try
diff --git a/WordAddIn/WordAddIn/ExpandOptionsForm.cs b/WordAddIn/WordAddIn/ExpandOptionsForm.cs
index d149a13..2501bf4 100644
--- a/WordAddIn/WordAddIn/ExpandOptionsForm.cs
+++ b/WordAddIn/WordAddIn/ExpandOptionsForm.cs
@@ -5,8 +5,18 @@ using System.Windows.Forms;
namespace WordAddIn
{
+ ///
+ /// 智能扩写设置窗体。
+ /// 功能描述:
+ /// 1. 提供多种预设的扩写模板(如"商务正式"、"细腻情感"等)。
+ /// 2. 允许用户修改预设模板或输入完全自定义的扩写要求。
+ /// 3. 返回用户的最终选择 (SelectedPrompt) 给调用方。
+ ///
public class ExpandOptionsForm : Form
{
+ ///
+ /// 用户最终确认的扩写要求。
+ ///
public string SelectedPrompt { get; private set; }
private ComboBox cmbTemplates;
@@ -32,6 +42,9 @@ namespace WordAddIn
InitializeTemplates();
}
+ ///
+ /// 初始化窗体组件。
+ ///
private void InitializeComponent()
{
this.lblTemplate = new System.Windows.Forms.Label();
@@ -124,6 +137,9 @@ namespace WordAddIn
}
+ ///
+ /// 初始化模板下拉列表。
+ ///
private void InitializeTemplates()
{
foreach (var key in _templates.Keys)
@@ -136,6 +152,10 @@ namespace WordAddIn
}
}
+ ///
+ /// 模板选择改变事件。
+ /// 当用户选择不同模板时,自动填充对应的 Prompt 到文本框中。
+ ///
private void CmbTemplates_SelectedIndexChanged(object sender, EventArgs e)
{
if (cmbTemplates.SelectedItem != null && _templates.ContainsKey(cmbTemplates.SelectedItem.ToString()))
@@ -144,6 +164,10 @@ namespace WordAddIn
}
}
+ ///
+ /// 确定按钮点击事件。
+ /// 验证输入有效性,并保存结果。
+ ///
private void BtnOk_Click(object sender, EventArgs e)
{
SelectedPrompt = txtCustomPrompt.Text.Trim();
diff --git a/WordAddIn/WordAddIn/ImageOptionsForm.cs b/WordAddIn/WordAddIn/ImageOptionsForm.cs
index 6d299a2..0f8a645 100644
--- a/WordAddIn/WordAddIn/ImageOptionsForm.cs
+++ b/WordAddIn/WordAddIn/ImageOptionsForm.cs
@@ -6,9 +6,23 @@ using WordAddIn.Properties;
namespace WordAddIn
{
+ ///
+ /// 生成配图设置窗体。
+ /// 功能描述:
+ /// 1. 允许用户选择图片风格(如写实、卡通、3D渲染等)。
+ /// 2. 允许用户输入额外的描述(如颜色、背景要求等)。
+ /// 3. 支持保存上次选择的风格,提升用户体验。
+ ///
public class ImageOptionsForm : Form
{
+ ///
+ /// 用户选择的图片风格。
+ ///
public string SelectedStyle { get; private set; }
+
+ ///
+ /// 用户输入的补充描述。
+ ///
public string AdditionalPrompt { get; private set; }
private ComboBox cmbStyles;
@@ -36,6 +50,9 @@ namespace WordAddIn
InitializeStyles();
}
+ ///
+ /// 初始化窗体组件。
+ ///
private void InitializeComponent()
{
this.lblStyle = new System.Windows.Forms.Label();
@@ -127,6 +144,9 @@ namespace WordAddIn
}
+ ///
+ /// 初始化风格列表,并尝试加载上次保存的风格。
+ ///
private void InitializeStyles()
{
cmbStyles.Items.AddRange(_styles);
@@ -143,6 +163,10 @@ namespace WordAddIn
}
}
+ ///
+ /// 确定按钮点击事件。
+ /// 获取用户选择,并保存风格到 Settings。
+ ///
private void BtnOk_Click(object sender, EventArgs e)
{
SelectedStyle = cmbStyles.SelectedItem.ToString();
diff --git a/WordAddIn/WordAddIn/LoadingForm.cs b/WordAddIn/WordAddIn/LoadingForm.cs
index 2b81631..a9ba344 100644
--- a/WordAddIn/WordAddIn/LoadingForm.cs
+++ b/WordAddIn/WordAddIn/LoadingForm.cs
@@ -5,6 +5,13 @@ using System.Windows.Forms;
namespace WordAddIn
{
+ ///
+ /// 加载动画窗体。
+ /// 功能描述:
+ /// 1. 在执行耗时操作(如 AI 生成)时显示,阻断用户界面。
+ /// 2. 使用 GDI+ 自定义绘制旋转动画 (Spinner)。
+ /// 3. 支持跨线程安全关闭。
+ ///
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);
+ ///
+ /// 构造函数。
+ ///
+ /// 加载时显示的提示文字
public LoadingForm(string message = "AI 正在思考中...")
{
InitializeComponent();
@@ -28,6 +39,9 @@ namespace WordAddIn
this.UpdateStyles();
}
+ ///
+ /// 初始化窗体组件。
+ ///
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
@@ -71,12 +85,18 @@ namespace WordAddIn
}
+ ///
+ /// 动画定时器触发事件。更新旋转角度并重绘。
+ ///
private void AnimationTimer_Tick(object sender, EventArgs e)
{
_spinnerAngle = (_spinnerAngle + 10) % 360;
this.Invalidate(); // 触发重绘
}
+ ///
+ /// 重写绘制事件,自定义绘制加载动画。
+ ///
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
@@ -127,7 +147,10 @@ namespace WordAddIn
g.ResetTransform();
}
- // 允许在非 UI 线程关闭窗体
+ ///
+ /// 线程安全的关闭方法。
+ /// 允许从非 UI 线程调用。
+ ///
public void CloseSafe()
{
if (this.IsDisposed) return;
@@ -143,7 +166,9 @@ namespace WordAddIn
}
}
- // 允许拖动窗体
+ ///
+ /// 支持鼠标拖动无边框窗体。
+ ///
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
diff --git a/WordAddIn/WordAddIn/LoginForm.cs b/WordAddIn/WordAddIn/LoginForm.cs
index 0845519..6ec18d2 100644
--- a/WordAddIn/WordAddIn/LoginForm.cs
+++ b/WordAddIn/WordAddIn/LoginForm.cs
@@ -4,6 +4,13 @@ using System.Windows.Forms;
namespace WordAddIn
{
+ ///
+ /// 用户登录窗体。
+ /// 功能描述:
+ /// 1. 模拟用户登录验证过程。
+ /// 2. 提供用户名和密码输入框。
+ /// 3. 目前使用硬编码的凭证 (huanghai / 12345678) 进行验证,实际生产中应连接认证服务。
+ ///
public class LoginForm : Form
{
private TextBox txtUsername;
@@ -19,6 +26,9 @@ namespace WordAddIn
InitializeComponent();
}
+ ///
+ /// 初始化窗体组件。
+ ///
private void InitializeComponent()
{
this.lblTitle = new System.Windows.Forms.Label();
@@ -135,6 +145,10 @@ namespace WordAddIn
}
+ ///
+ /// 登录按钮点击事件。
+ /// 验证用户名和密码。
+ ///
private void BtnLogin_Click(object sender, EventArgs e)
{
string user = txtUsername.Text.Trim();
diff --git a/WordAddIn/WordAddIn/Models.cs b/WordAddIn/WordAddIn/Models.cs
index aab1d77..73088ab 100644
--- a/WordAddIn/WordAddIn/Models.cs
+++ b/WordAddIn/WordAddIn/Models.cs
@@ -3,70 +3,208 @@ using System.Collections.Generic;
namespace WordAddIn.Models
{
- // Chat Completion Models
+ // ==========================================
+ // Chat Completion API 模型定义
+ // 用于与 OpenAI / Gemini 兼容的聊天接口交互
+ // ==========================================
+
+ ///
+ /// 聊天补全请求对象
+ ///
public class ChatCompletionRequest
{
+ ///
+ /// 模型名称 (e.g., "gpt-3.5-turbo", "gemini-3-flash")
+ ///
public string model { get; set; } = "gpt-3.5-turbo";
+
+ ///
+ /// 消息列表,包含 system, user, assistant 等角色的消息
+ ///
public List messages { get; set; }
+
+ ///
+ /// 温度参数,控制生成的随机性 (0.0 - 1.0)
+ /// 较高值使输出更随机,较低值使输出更确定
+ ///
public double temperature { get; set; } = 0.7;
}
+ ///
+ /// 聊天消息对象
+ ///
public class Message
{
+ ///
+ /// 角色: system, user, assistant
+ ///
public string role { get; set; }
+
+ ///
+ /// 消息内容
+ ///
public string content { get; set; }
}
+ ///
+ /// 聊天补全响应对象
+ ///
public class ChatCompletionResponse
{
+ ///
+ /// 响应 ID
+ ///
public string id { get; set; }
+
+ ///
+ /// 生成的选择列表 (通常只取第一个)
+ ///
public List choices { get; set; }
}
+ ///
+ /// 生成结果选项
+ ///
public class Choice
{
+ ///
+ /// 生成的消息内容
+ ///
public Message message { get; set; }
}
- // Image Generation Models
+ // ==========================================
+ // Image Generation API 模型定义
+ // 用于与 DALL-E / Gemini Image 接口交互
+ // ==========================================
+
+ ///
+ /// 图片生成请求对象
+ ///
public class ImageGenerationRequest
{
+ ///
+ /// 模型名称
+ ///
public string model { get; set; } = "dall-e-3";
+
+ ///
+ /// 绘图提示词
+ ///
public string prompt { get; set; }
+
+ ///
+ /// 生成数量 (DALL-E 3 通常限制为 1)
+ ///
public int n { get; set; } = 1;
+
+ ///
+ /// 图片尺寸 (e.g., "1024x1024")
+ ///
public string size { get; set; } = "1024x1024";
}
+ ///
+ /// 图片生成响应对象
+ ///
public class ImageGenerationResponse
{
+ ///
+ /// 生成的数据列表
+ ///
public List data { get; set; }
}
+ ///
+ /// 图片数据项
+ ///
public class ImageItem
{
+ ///
+ /// 图片 URL
+ ///
public string url { get; set; }
}
- // Outline Models
+ // ==========================================
+ // 业务逻辑模型定义
+ // 用于大纲生成和样式配置
+ // ==========================================
+
+ ///
+ /// 大纲条目
+ /// 用于接收 AI 生成的结构化大纲数据
+ ///
public class OutlineItem
{
+ ///
+ /// 标题文本
+ ///
public string Title { get; set; }
- public int Level { get; set; } // 0=MainTitle, 1=H1, 2=H2, 3=H3
+
+ ///
+ /// 标题层级
+ /// 0: 主标题
+ /// 1: 一级标题
+ /// 2: 二级标题
+ /// 3: 三级标题
+ ///
+ public int Level { get; set; }
}
+ ///
+ /// 大纲样式配置集合
+ /// 存储各级标题的样式设置
+ ///
public class OutlineStyleConfig
{
- public TextStyle MainTitle { get; set; } = new TextStyle { FontSize = 22, IsBold = true, FontName = "微软雅黑", Alignment = 1 }; // Center
+ ///
+ /// 主标题样式
+ ///
+ public TextStyle MainTitle { get; set; } = new TextStyle { FontSize = 22, IsBold = true, FontName = "微软雅黑", Alignment = 1 }; // 默认为居中
+
+ ///
+ /// 一级标题样式
+ ///
public TextStyle Heading1 { get; set; } = new TextStyle { FontSize = 16, IsBold = true, FontName = "黑体" };
+
+ ///
+ /// 二级标题样式
+ ///
public TextStyle Heading2 { get; set; } = new TextStyle { FontSize = 14, IsBold = true, FontName = "黑体" };
+
+ ///
+ /// 三级标题样式
+ ///
public TextStyle Heading3 { get; set; } = new TextStyle { FontSize = 12, IsBold = true, FontName = "宋体" };
}
+ ///
+ /// 文本样式定义
+ ///
public class TextStyle
{
+ ///
+ /// 字体名称
+ ///
public string FontName { get; set; } = "宋体";
+
+ ///
+ /// 字号 (磅值)
+ ///
public float FontSize { get; set; } = 12;
+
+ ///
+ /// 是否加粗
+ ///
public bool IsBold { get; set; } = false;
- public int Alignment { get; set; } = 0; // 0=Left, 1=Center, 2=Right
+
+ ///
+ /// 对齐方式
+ /// 0: 左对齐
+ /// 1: 居中
+ /// 2: 右对齐
+ ///
+ public int Alignment { get; set; } = 0;
}
}
diff --git a/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs b/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs
index b8ca892..1f386a0 100644
--- a/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs
+++ b/WordAddIn/WordAddIn/ReferenceGeneratorForm.cs
@@ -5,6 +5,13 @@ using System.IO;
namespace WordAddIn
{
+ ///
+ /// 素材写作(参考生成)配置窗体。
+ /// 功能描述:
+ /// 1. 允许用户提供参考素材,用于辅助 AI 生成内容。
+ /// 2. 支持两种输入模式:上传文件 (Word/Txt) 或 直接输入文本。
+ /// 3. 允许用户设置生成内容的字数上限。
+ ///
public class ReferenceGeneratorForm : Form
{
private TabControl tabControl;
@@ -20,9 +27,24 @@ namespace WordAddIn
private Label lblWordCount;
private Label lblWordUnit;
+ ///
+ /// 获取直接输入的参考文本内容。
+ ///
public string ReferenceContent { get; private set; }
+
+ ///
+ /// 获取用户设置的字数上限。
+ ///
public int WordLimit { get; private set; }
+
+ ///
+ /// 指示是否选择了文件模式。
+ ///
public bool IsFileMode { get; private set; }
+
+ ///
+ /// 获取用户选择的文件路径。
+ ///
public string SelectedFilePath { get; private set; }
public ReferenceGeneratorForm()
@@ -30,6 +52,9 @@ namespace WordAddIn
InitializeComponent();
}
+ ///
+ /// 初始化窗体组件。
+ ///
private void InitializeComponent()
{
this.tabControl = new System.Windows.Forms.TabControl();
@@ -235,6 +260,10 @@ namespace WordAddIn
}
+ ///
+ /// 浏览按钮点击事件。
+ /// 打开文件选择对话框,选择参考文档。
+ ///
private void BtnBrowse_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
@@ -248,6 +277,10 @@ namespace WordAddIn
}
}
+ ///
+ /// 生成按钮点击事件。
+ /// 验证输入有效性,并保存用户的选择。
+ ///
private void BtnGenerate_Click(object sender, EventArgs e)
{
WordLimit = (int)numWordCount.Value;
diff --git a/WordAddIn/WordAddIn/ThisAddIn.cs b/WordAddIn/WordAddIn/ThisAddIn.cs
index 7840883..cf97ac2 100644
--- a/WordAddIn/WordAddIn/ThisAddIn.cs
+++ b/WordAddIn/WordAddIn/ThisAddIn.cs
@@ -1,7 +1,13 @@
-namespace WordAddIn
+namespace WordAddIn
{
public partial class ThisAddIn
{
+ ///
+ /// 覆盖 CreateRibbonExtensibilityObject 方法。
+ /// 返回我们自定义的 Ribbon 实例 (AiRibbon)。
+ /// VSTO 在加载时会调用此方法来获取 Ribbon XML 和回调处理程序。
+ ///
+ /// IRibbonExtensibility 接口实现
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 生成的代码
diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll
index ca6626f..bc85c99 100644
Binary files a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll and b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll differ
diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest
index 555d244..8c51ce4 100644
--- a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest
+++ b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.dll.manifest
@@ -166,14 +166,14 @@
-
+
- kaNoiBD64kA337KwwCyQswknaKN/Jm2xVBMG2u/zfjM=
+ 57KGd7UXAGzFPWql99dRDRDZXoe2StLuM7Uyd52MAcU=
@@ -200,4 +200,4 @@
-gCkchTa6A61vV45e2JsTggjhXU+MDFhh5zWDEljDxY4=KT7wTPexe3IeF5T7gn7yChiy5gXySA8+kakdFMgJMYZy1G0xI5ojr1bKiB4m9l0kPeNjDMhD7NSSpUOhyyT57CsjriDL2JN6pzRIItHEfIyUz5kwUkQoPDpebaVMWeXxIy0fbrT691Z7c1AC2BSG3uVwq487YB1CqqzufpclDqo=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorrIvGDs7aJWd5U9eWWhzLMnDB7NI0COaBGFP9BeGNrj8=ExruAcyG+I6xR7ACNNi1O9NI+7HWanTyJDMNa4Dh6G61sVw1AOOkfrrKJlGEy43t/XpubZu90wviQu7/Fkf/02rrYxUjhmP6IyCiMQLuTkDPm01+ExgWzIxjufW0lOYEsr5KvXJsLMKKKx4qAYoz+hd3tIzB01lwKqvSwkXP1sY=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==
\ No newline at end of file
+Q59Lub3/7zQPl9a5u6WlazdugvzYLbuMA2ScUeG2Kgc=e+fTQJmSYA2Ba8atHBYYQHQ3yumSSKoHkFWQhSnBCeAJ3fJdgAe1BT50EjnkKWP6bgL7txi2fDWiqXH7PomA+4D+t9Px5E2mRjSn6TocihjON9kFjatapE5bYrXX4rxZmsa7baDmtRolKjErgeei1cgkLG+00xgJkUDvKwgLaZM=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorBowY9B2/AdRMLaAmKgcs3z/FtyzUeliTzB6MSGgWv2w=m1b3Quuv79UlTgmBro71Cl0hJmhy+RrFxAwWuFPtCzZmP7iI7KNCfbMMwSWufjzNYW/VF9ShKMk5nWLC1Q7iujoTWIYyUuiIDawYYrTyK4gO3QgQjE06b5DoluCNz4gENwYt2WJvNGRewiLCIEnDZhpAm/BaMJkSbYAkD22ASIY=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==
\ No newline at end of file
diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb
index cd59581..bcfbd05 100644
Binary files a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb and b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.pdb differ
diff --git a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto
index fed3e92..751f28c 100644
--- a/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto
+++ b/WordAddIn/WordAddIn/bin/Debug/WordAddIn.vsto
@@ -14,8 +14,8 @@
- PPmVr+YsqgTyziCPQl2d+8CBQOBV0DzcZLxUnUr5dGA=
+ kQ6lvB1J+RPqWmN3TV1FKCJZwSRY6p4lkcGyY6yVSYM=
-gmWu531mSYE6e+478tWijNz+HJGL6Ub2q/oN99Yoq8g=S6nctbanxh4cDuBGhiBntmVFtWyc/iVYn8rSFOXQEh7EgUG7v9FMK2qtYi6up36d5NWKvuV+kzKs5uOuUIg1P/hBWLVaRLzQSUQC9vekWmdUcrYjuQGrN7eqfJSK/H9QA1W9pbJ8KEDHQB82v9YQGVLhCn5Q3AxGlAWmaKQETZM=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\AdministratorBUiNKpXwHd6U/7xnyou3wQDJxj8V+19+IoADYwPgppY=a2Wc9bjZ7YYsiAqAYwyYkYVJWXxl68YxMoNuB4DYMbo9uBiuMK+fMHN4ko9mqesTW3u7cxiI+HCZgPrEfG7yJdHo67+qdoXFThYYCvD0kCRLPM2s44twfV4pFtiGn5NZtoltrmevUR6pS9VFUAiYtKTygKHsU7CjXfa3UfTUV3c=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==
\ No newline at end of file
+nO4LOJBDcGxL8l8R9b9+yIX5TXJ4Ym9VOMcF/Sry+qw=ZXK46Htr5iGt1kgI+VOKE9t9CPf3Xfkp16PHJxaK38qkTVXu/oylz4UUYGk/Tf1JuEkuQq06zN6CYCma0rwL4Gyoo/5n09Xzg3zmriPEIS2cs8BPbXwH90FbQ0HDaPVM+dJ9A8c+KTH9h6zNm8aj/HQdBRamLUv1D2PBxk7NE1s=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABCN=DESKTOP-Q6H7B6L\Administrator9TyNXNxi0FyDrOMgoJ3zDQERhOeW01TflRltAh+X1k8=DHwRL4z0MQqHXYXDBnMyS39H5LqOPVJmaNB+zmQ+YoMUdVQojZBKuqSvpw3WvvWLhmP5dAH92Q94xrTd0gGggHPmL2r5/puOuDzYIhrvSXhE5dVoUwv8GW5Lem+FjDcAYn6TFic2CEkvwOE9FKEfT92ijcaa4K8AEWPm8X64EKg=23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=AQABMIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==
\ No newline at end of file
diff --git a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll
index ca6626f..bc85c99 100644
Binary files a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll and b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.dll differ
diff --git a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb
index cd59581..bcfbd05 100644
Binary files a/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb and b/WordAddIn/WordAddIn/obj/Debug/WordAddIn.pdb differ