Commit e7dabb90 authored by 段廷瑞's avatar 段廷瑞

feat: 工具平台支持Claude Code对话(基于本地命令行)

parent 27316c1c
...@@ -59,3 +59,5 @@ bin ...@@ -59,3 +59,5 @@ bin
/AppHome/config/List`1.json /AppHome/config/List`1.json
/Tools/Tools.csproj.user /Tools/Tools.csproj.user
/Tools/.claude/settings.local.json /Tools/.claude/settings.local.json
/.claude/settings.local.json
/AppHome/ClaudeChat
using System;
using System.Collections.Generic;
namespace Tools.MyCommon
{
public sealed class ChatMessage
{
public string Role { get; set; }
public string Content { get; set; }
public string ThinkingContent { get; set; }
public bool IsStreaming { get; set; }
public DateTime Timestamp { get; set; } = DateTime.Now;
}
public sealed class AskUserQuestionInfo
{
public string ToolUseId { get; set; }
public List<QuestionItem> Questions { get; set; } = new List<QuestionItem>();
}
public sealed class QuestionItem
{
public string Question { get; set; }
public string Header { get; set; }
public List<OptionItem> Options { get; set; } = new List<OptionItem>();
public bool MultiSelect { get; set; }
}
public sealed class OptionItem
{
public string Label { get; set; }
public string Description { get; set; }
}
}
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Tools.MyCommon
{
public sealed class ChatSession
{
public string Id { get; set; }
public string Title { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime SavedAt { get; set; }
public List<ChatMessage> Messages { get; set; }
}
public static class HistoryManager
{
public static string EnsureHistoryDir(string workingDirectory)
{
var dir = Path.Combine(workingDirectory, "history");
Directory.CreateDirectory(dir);
return dir;
}
public static void SaveSession(string historyDir, List<ChatMessage> messages)
{
if (messages == null || messages.Count == 0) return;
var userMessages = messages.Where(m => m.Role == "user").ToList();
if (userMessages.Count == 0) return;
var session = new ChatSession
{
Id = DateTime.Now.ToString("yyyyMMdd_HHmmss"),
Title = Truncate(userMessages[0].Content, 60),
CreatedAt = messages[0].Timestamp,
SavedAt = DateTime.Now,
Messages = new List<ChatMessage>(messages)
};
var filePath = Path.Combine(historyDir, $"session_{session.Id}.json");
var json = JsonConvert.SerializeObject(session, Formatting.Indented);
File.WriteAllText(filePath, json);
}
public static ChatSession LoadSession(string filePath)
{
var json = File.ReadAllText(filePath);
return JsonConvert.DeserializeObject<ChatSession>(json);
}
public static List<SessionSummary> ListSessions(string historyDir)
{
if (!Directory.Exists(historyDir))
return new List<SessionSummary>();
var result = new List<SessionSummary>();
var files = Directory.GetFiles(historyDir, "session_*.json");
foreach (var file in files)
{
try
{
var session = LoadSession(file);
result.Add(new SessionSummary
{
FilePath = file,
Id = session.Id,
Title = session.Title,
CreatedAt = session.CreatedAt,
MessageCount = session.Messages?.Count ?? 0
});
}
catch { }
}
result.Sort((a, b) => b.CreatedAt.CompareTo(a.CreatedAt));
return result;
}
public static void DeleteSession(string filePath)
{
if (File.Exists(filePath))
File.Delete(filePath);
}
private static string Truncate(string text, int maxLen)
{
if (string.IsNullOrEmpty(text)) return "";
text = text.Replace('\r', ' ').Replace('\n', ' ');
return text.Length <= maxLen ? text : text.Substring(0, maxLen) + "...";
}
}
public sealed class SessionSummary
{
public string FilePath { get; set; }
public string Id { get; set; }
public string Title { get; set; }
public DateTime CreatedAt { get; set; }
public int MessageCount { get; set; }
public string DisplayText =>
$"{CreatedAt:yyyy-MM-dd HH:mm} [{MessageCount}条] {Title}";
}
}
This diff is collapsed.
This diff is collapsed.
...@@ -84,6 +84,10 @@ ...@@ -84,6 +84,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MyCommon\Log.cs" /> <Compile Include="MyCommon\Log.cs" />
<Compile Include="MyCommon\ChatMessage.cs" />
<Compile Include="MyCommon\ClaudeCliService.cs" />
<Compile Include="MyCommon\ChatSession.cs" />
<Compile Include="MyCommon\MarkdownRenderer.cs" />
<Compile Include="MyCommon\UserLog.cs" /> <Compile Include="MyCommon\UserLog.cs" />
<Compile Include="MyCommon\controls\MyTreeView.cs"> <Compile Include="MyCommon\controls\MyTreeView.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
...@@ -245,6 +249,12 @@ ...@@ -245,6 +249,12 @@
<Compile Include="UI\wAutoClass.Designer.cs"> <Compile Include="UI\wAutoClass.Designer.cs">
<DependentUpon>wAutoClass.cs</DependentUpon> <DependentUpon>wAutoClass.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="UI\wClaudeChat.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\wClaudeChat.Designer.cs">
<DependentUpon>wClaudeChat.cs</DependentUpon>
</Compile>
<Compile Include="UI\Models\AppModel.cs" /> <Compile Include="UI\Models\AppModel.cs" />
<Compile Include="MyCommon\Common.cs" /> <Compile Include="MyCommon\Common.cs" />
<Compile Include="MyCommon\wBasic.cs"> <Compile Include="MyCommon\wBasic.cs">
...@@ -374,6 +384,10 @@ ...@@ -374,6 +384,10 @@
<Compile Include="wMain.Designer.cs"> <Compile Include="wMain.Designer.cs">
<DependentUpon>wMain.cs</DependentUpon> <DependentUpon>wMain.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="wMain.ClaudeChat.cs">
<DependentUpon>wMain.cs</DependentUpon>
<SubType>Form</SubType>
</Compile>
<EmbeddedResource Include="MyCommon\controls\ucAudit.resx"> <EmbeddedResource Include="MyCommon\controls\ucAudit.resx">
<DependentUpon>ucAudit.cs</DependentUpon> <DependentUpon>ucAudit.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
...@@ -444,6 +458,9 @@ ...@@ -444,6 +458,9 @@
<EmbeddedResource Include="UI\sys\wUploadUpdate.resx"> <EmbeddedResource Include="UI\sys\wUploadUpdate.resx">
<DependentUpon>wUploadUpdate.cs</DependentUpon> <DependentUpon>wUploadUpdate.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="UI\wClaudeChat.resx">
<DependentUpon>wClaudeChat.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UI\wExcelExportSetting.resx"> <EmbeddedResource Include="UI\wExcelExportSetting.resx">
<DependentUpon>wExcelExportSetting.cs</DependentUpon> <DependentUpon>wExcelExportSetting.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
......
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="tmrFrog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>
\ No newline at end of file
using System.Windows.Forms;
using Tools.UI;
namespace Tools
{
partial class wMain
{
private wClaudeChat _claudeChat;
private void InitClaudeChat()
{
_claudeChat = new wClaudeChat
{
TopLevel = false,
FormBorderStyle = FormBorderStyle.None,
Dock = DockStyle.Fill,
};
HomePage.Controls.Clear();
HomePage.Controls.Add(_claudeChat);
_claudeChat.Show();
}
}
}
...@@ -66,11 +66,6 @@ ...@@ -66,11 +66,6 @@
this.timer1 = new System.Windows.Forms.Timer(this.components); this.timer1 = new System.Windows.Forms.Timer(this.components);
this.tabControl1 = new Tools.MyCommon.controls.TabControlEx(); this.tabControl1 = new Tools.MyCommon.controls.TabControlEx();
this.HomePage = new System.Windows.Forms.TabPage(); this.HomePage = new System.Windows.Forms.TabPage();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.lbTellUS = new System.Windows.Forms.Label();
this.lbUserAck = new System.Windows.Forms.Label();
this.lbBrief = new System.Windows.Forms.Label();
this.label9 = new System.Windows.Forms.Label();
this.panel1.SuspendLayout(); this.panel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel1.SuspendLayout();
...@@ -80,8 +75,6 @@ ...@@ -80,8 +75,6 @@
this.Header.SuspendLayout(); this.Header.SuspendLayout();
this.Footer.SuspendLayout(); this.Footer.SuspendLayout();
this.tabControl1.SuspendLayout(); this.tabControl1.SuspendLayout();
this.HomePage.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// panel1 // panel1
...@@ -471,7 +464,6 @@ ...@@ -471,7 +464,6 @@
// //
// HomePage // HomePage
// //
this.HomePage.Controls.Add(this.tableLayoutPanel1);
this.HomePage.Location = new System.Drawing.Point(4, 26); this.HomePage.Location = new System.Drawing.Point(4, 26);
this.HomePage.Name = "HomePage"; this.HomePage.Name = "HomePage";
this.HomePage.Padding = new System.Windows.Forms.Padding(3); this.HomePage.Padding = new System.Windows.Forms.Padding(3);
...@@ -480,85 +472,6 @@ ...@@ -480,85 +472,6 @@
this.HomePage.Text = "首页"; this.HomePage.Text = "首页";
this.HomePage.UseVisualStyleBackColor = true; this.HomePage.UseVisualStyleBackColor = true;
// //
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 5;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.Controls.Add(this.lbTellUS, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.lbUserAck, 3, 1);
this.tableLayoutPanel1.Controls.Add(this.lbBrief, 1, 3);
this.tableLayoutPanel1.Controls.Add(this.label9, 3, 3);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 5;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 30F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 30F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(480, 333);
this.tableLayoutPanel1.TabIndex = 0;
//
// lbTellUS
//
this.lbTellUS.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(192)))));
this.lbTellUS.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbTellUS.Font = new System.Drawing.Font("微软雅黑", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.lbTellUS.ForeColor = System.Drawing.Color.Black;
this.lbTellUS.Location = new System.Drawing.Point(115, 93);
this.lbTellUS.Margin = new System.Windows.Forms.Padding(0);
this.lbTellUS.Name = "lbTellUS";
this.lbTellUS.Size = new System.Drawing.Size(115, 62);
this.lbTellUS.TabIndex = 0;
this.lbTellUS.Text = "需求反馈";
this.lbTellUS.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// lbUserAck
//
this.lbUserAck.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(192)))), ((int)(((byte)(255)))));
this.lbUserAck.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbUserAck.Font = new System.Drawing.Font("微软雅黑", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.lbUserAck.ForeColor = System.Drawing.Color.Black;
this.lbUserAck.Location = new System.Drawing.Point(250, 93);
this.lbUserAck.Margin = new System.Windows.Forms.Padding(0);
this.lbUserAck.Name = "lbUserAck";
this.lbUserAck.Size = new System.Drawing.Size(115, 62);
this.lbUserAck.TabIndex = 1;
this.lbUserAck.Text = "需求受理";
this.lbUserAck.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// lbBrief
//
this.lbBrief.BackColor = System.Drawing.Color.PaleTurquoise;
this.lbBrief.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbBrief.Font = new System.Drawing.Font("微软雅黑", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.lbBrief.ForeColor = System.Drawing.Color.Black;
this.lbBrief.Location = new System.Drawing.Point(115, 175);
this.lbBrief.Margin = new System.Windows.Forms.Padding(0);
this.lbBrief.Name = "lbBrief";
this.lbBrief.Size = new System.Drawing.Size(115, 62);
this.lbBrief.TabIndex = 2;
this.lbBrief.Text = "平台简介";
this.lbBrief.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label9
//
this.label9.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(128)))));
this.label9.Dock = System.Windows.Forms.DockStyle.Fill;
this.label9.Font = new System.Drawing.Font("微软雅黑", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label9.ForeColor = System.Drawing.Color.White;
this.label9.Location = new System.Drawing.Point(250, 175);
this.label9.Margin = new System.Windows.Forms.Padding(0);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(115, 62);
this.label9.TabIndex = 3;
this.label9.Text = "敬请期待";
this.label9.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
// //
// wMain // wMain
// //
...@@ -583,8 +496,6 @@ ...@@ -583,8 +496,6 @@
this.Header.ResumeLayout(false); this.Header.ResumeLayout(false);
this.Footer.ResumeLayout(false); this.Footer.ResumeLayout(false);
this.tabControl1.ResumeLayout(false); this.tabControl1.ResumeLayout(false);
this.HomePage.ResumeLayout(false);
this.tableLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
} }
...@@ -625,11 +536,6 @@ ...@@ -625,11 +536,6 @@
private System.Windows.Forms.ToolStripMenuItem tsmiSysUpdateLog; private System.Windows.Forms.ToolStripMenuItem tsmiSysUpdateLog;
private Tools.MyCommon.controls.TabControlEx tabControl1; private Tools.MyCommon.controls.TabControlEx tabControl1;
private System.Windows.Forms.TabPage HomePage; private System.Windows.Forms.TabPage HomePage;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label lbTellUS;
private System.Windows.Forms.Label lbUserAck;
private System.Windows.Forms.Label lbBrief;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.ToolStripMenuItem tsmiShowFetetoWork; private System.Windows.Forms.ToolStripMenuItem tsmiShowFetetoWork;
private System.Windows.Forms.Button btnToggleMenu; private System.Windows.Forms.Button btnToggleMenu;
} }
......
...@@ -86,10 +86,8 @@ namespace Tools ...@@ -86,10 +86,8 @@ namespace Tools
this.tabControl1.MouseDoubleClick += TabControl1_MouseDoubleClick; this.tabControl1.MouseDoubleClick += TabControl1_MouseDoubleClick;
//用户登录 //用户登录
this.lbSysLoginUser.Click += LbSysLoginUser_Click; this.lbSysLoginUser.Click += LbSysLoginUser_Click;
//扩展:首页功能 //初始化Claude Chat
this.lbTellUS.Click += LbTellUS_Click; this.InitClaudeChat();
this.lbUserAck.Click += LbUserAck_Click;
this.lbBrief.Click += LbBrief_Click;
//检查更新 //检查更新
Task.Run(() => this.CheckUpdate(true)); Task.Run(() => this.CheckUpdate(true));
//加载首页 //加载首页
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment