giteaCICD

This commit is contained in:
SpecialX
2025-05-27 11:58:36 +08:00
parent d36fef2bbb
commit 95daf9471b
9 changed files with 200 additions and 32 deletions

14
.gitea/workflows/ci.yaml Normal file
View File

@@ -0,0 +1,14 @@
name: Tech
on: [push] # 当有新的push事件发生时触发此工作流程
jobs:
explore-gitea-actions:
runs-on: Tech
steps:
- uses: actions/checkout@v4 # 使用actions/checkout来克隆您的仓库代码
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}

View File

@@ -92,7 +92,7 @@
"含在 <SQ> 内部,包含一个或多个 <O>。<O> (选项,必需) 包含在 <Os> 内部,具有 V (必需) 属性。"; "含在 <SQ> 内部,包含一个或多个 <O>。<O> (选项,必需) 包含在 <Os> 内部,具有 V (必需) 属性。";
public static string BreakQuestions = "请识别以下文本中的每一道大题将其转换为XML格式你只需要给出转换后的结果,不需要其他任何无关的输出,其中 <EP> 是XML的根元素。用<Q> 和</Q> 标记来包裹每一道大题。请确保标记后的文本保持原始格式和内容。"; public static string BreakQuestions = "请识别下面给出的试卷内容,分离并返回每一道大题,并将其转换为XML格式你只需要给出转换后的结果,不需要其他任何无关的输出,其中 <EP> 是XML的根元素。用<Q> 和</Q> 标记来包裹每一道大题。请确保标记后的文本保持原始格式和内容。";
public static string ParseSignelQuestion = "请将以下提供的一道大题内容,转换为符合以下 C# 类结构的 XML 格式,你只需要给出转换后的结果,不需要其他任何无关的输出:" + public static string ParseSignelQuestion = "请将以下提供的一道大题内容,转换为符合以下 C# 类结构的 XML 格式,你只需要给出转换后的结果,不需要其他任何无关的输出:" +
@@ -104,5 +104,70 @@
" <SQ> 内部的 <Os> 元素列表中的 <O> 元素。如果子题目有选项,则将它们包裹在 <Os> 元素中。对于每个 <O> 元素,填充 V 属性V对应 Option.Value" + " <SQ> 内部的 <Os> 元素列表中的 <O> 元素。如果子题目有选项,则将它们包裹在 <Os> 元素中。对于每个 <O> 元素,填充 V 属性V对应 Option.Value" +
"(从选项内容中提取)。嵌套题组:如果大题内部包含其他大题,则作为 <QG> 内部的 <SQGs> 元素列表中的 <QG> 元素(即嵌套 <QG>)。" + "(从选项内容中提取)。嵌套题组:如果大题内部包含其他大题,则作为 <QG> 内部的 <SQGs> 元素列表中的 <QG> 元素(即嵌套 <QG>)。" +
"请确保生成的 XML 严格遵循上述结构和命名约定,以方便 C# XmlSerializer 进行反序列化。"; "请确保生成的 XML 严格遵循上述结构和命名约定,以方便 C# XmlSerializer 进行反序列化。";
public static string ParseSignelQuestion2 =
@"请将给定的文本按下面的xml格式要求转换,在返回结果时,只返回XML转换内容,不要输出其他内容,如果给你的文本不能提取有效文本,请直接返回<QG/>,下面是格式:
<QG>
<T>XML生成规则</T>
<SQGs>
<QG>
<T>1. 根元素与题目组嵌套</T>
<Os>
<O V=""根元素必须为<QG>。""/>
<O V=""所有题目组通过<SQGs>嵌套。""/>
<O V=""当题目组不包含子题目组时,直接使用<SQs>包裹单个题目<SQ>,不应再嵌套<SQGs>。""/>
</Os>
</QG>
<QG>
<T>2. 原文引用规范</T>
<Os>
<O V=""阅读理解等需要上下文的题目组,原文内容需完整嵌入<QR>标签中。此标签仅在题目组QG级别使用。""/>
<O V=""示例:
<QG>
<T> 标题 </T>
<QR> 引用正文 </QR>
<SQs>
<SQ><T>题目</T>
<SA> 答案 </SA>
</SQ>
</SQs>
</QG>""/>
</Os>
</QG>
<QG>
<T>3. 题目封装与答案存储</T>
<Os>
<O V=""单选题:<SQ><T>...</T><Os><O V='A.'/><O V='B.'/>...</Os><SA>B</SA></SQ>""/>
<O V=""简答题:<SQ><T>...</T><SA>...</SA></SQ>""/>
<O V=""填空题:<SQ><T>...</T><SA>答案1&#10;答案2</SA></SQ>""/>
<O V=""简答题必须保留所有修饰语____标注并将其包含在<T>或<SQ><T>中。""/>
<O V=""多行答案(如简答题或填空题的多个空)使用换行符&#10;分隔。""/>
</Os>
</QG>
<QG>
<T>4. 选项与答案值</T>
<Os>
<O V=""<Os>仅用于存储题目中出现的选项,其顺序严格按题目原有的顺序排列。""/>
<O V=""<O V=''/'>表示空选项值(如有需要,通常不会出现于实际选项中)。""/>
<O V=""<SA>标签用于存储题目的正确答案。单选题的<SA>应存储正确选项的字母(如'B')。""/>
</Os>
</QG>
</SQGs>
<SQs>
<SQ><T>当不包含子题目时</T>
<Os>
<O V=""直接使用<SQs>包裹每个题目<SQ>,不再嵌套<SQGs>。""/>
</Os>
</SQ>
</SQs>
</QG>";
} }
} }

View File

@@ -35,17 +35,13 @@ namespace TechHelper.Client.Exam
public string Id { get; set; } public string Id { get; set; }
[JsonProperty("标题")] [JsonProperty("标题")]
[XmlAttribute("T")] // T for Title [XmlElement("T")] // T for Title
public string Title { get; set; } public string Title { get; set; }
[JsonProperty("分值")] [JsonProperty("分值")]
[XmlAttribute("S")] // S for Score [XmlAttribute("S")] // S for Score
public int Score { get; set; } public int Score { get; set; }
[JsonProperty("分值问题标记")]
[XmlAttribute("SPM")] // SPM for ScoreProblemMarker
public string ScoreProblemMarker { get; set; } = ""; // 初始化为空字符串,避免 null
[JsonProperty("题目引用")] [JsonProperty("题目引用")]
[XmlElement("QR")] // QR for QuestionReference作为 <QR> 元素 [XmlElement("QR")] // QR for QuestionReference作为 <QR> 元素
public string QuestionReference { get; set; } = ""; // 初始化为空字符串 public string QuestionReference { get; set; } = ""; // 初始化为空字符串
@@ -69,26 +65,20 @@ namespace TechHelper.Client.Exam
public string SubId { get; set; } public string SubId { get; set; }
[JsonProperty("题干")] [JsonProperty("题干")]
[XmlAttribute("T")] // T for Text (Stem) [XmlElement("T")] // T for Text (Stem)
public string Stem { get; set; } public string Stem { get; set; }
[JsonProperty("分值")] [JsonProperty("分值")]
[XmlAttribute("S")] // S for Score [XmlAttribute("S")] // S for Score
public int Score { get; set; } // 分值通常为整数 public int Score { get; set; } // 分值通常为整数
[JsonProperty("分值问题标记")]
[XmlAttribute("SPM")] // SPM for ScoreProblemMarker
public string ScoreProblemMarker { get; set; } = "";
// 注意:这里的 Options 需要适配 XML 结构 <Os><O V="X"/></Os>
// 因此它不再是 List<string>,而是 List<Option>
[JsonProperty("选项")] [JsonProperty("选项")]
[XmlArray("Os")] // Os 包含 <O> 列表 [XmlArray("Os")] // Os 包含 <O> 列表
[XmlArrayItem("O")] [XmlArrayItem("O")]
public List<Option> Options { get; set; } = new List<Option>(); public List<Option> Options { get; set; } = new List<Option>();
[JsonProperty("示例答案")] [JsonProperty("示例答案")]
[XmlAttribute("SA")] // SA for SampleAnswer [XmlElement("SA")] // SA for SampleAnswer
public string SampleAnswer { get; set; } = ""; public string SampleAnswer { get; set; } = "";
} }

View File

@@ -0,0 +1,36 @@
using System.Xml.Serialization;
namespace TechHelper.Client.Exam.QuestionSimple
{
[XmlRoot("QG")]
public class QuestionGroup
{
[XmlElement("T")]
public string Title { get; set; }
[XmlElement("QR")]
public string QuestionReference { get; set; } = "";
[XmlArray("SQs")]
[XmlArrayItem("SQ")]
public List<SubQuestion> SubQuestions { get; set; } = new List<SubQuestion>();
[XmlArray("SQGs")]
[XmlArrayItem("QG")]
public List<QuestionGroup> SubQuestionGroups { get; set; } = new List<QuestionGroup>();
}
public class SubQuestion
{
[XmlElement("T")]
public string Stem { get; set; }
[XmlArray("Os")]
[XmlArrayItem("O")]
public List<Option> Options { get; set; } = new List<Option>();
[XmlElement("SA")]
public string SampleAnswer { get; set; } = "";
}
public class Option
{
[XmlAttribute("V")]
public string Value { get; set; }
}
}

View File

@@ -21,6 +21,7 @@
<MudButton OnClick="ReCorrectXMLAsync">AplyAIResult</MudButton> <MudButton OnClick="ReCorrectXMLAsync">AplyAIResult</MudButton>
<MudButton OnClick="ReCorrectXMLAsync">Save</MudButton> <MudButton OnClick="ReCorrectXMLAsync">Save</MudButton>
<MudButton OnClick="ReCorrectXMLAsync">Public</MudButton> <MudButton OnClick="ReCorrectXMLAsync">Public</MudButton>
<MudButton OnClick="CopyToClipboard">Copy</MudButton>
</MudButtonGroup> </MudButtonGroup>
</MudPaper> </MudPaper>
@@ -57,8 +58,17 @@
@for (int i = 0; i < ParseResult.Count; i++) @for (int i = 0; i < ParseResult.Count; i++)
{ {
int index = i; int index = i;
<MudCard Style="background-color:#ffffff88">
<MudTextField Class="ma-3" AutoGrow="true" @bind-Value="ParseResult[index]"></MudTextField> <MudCardHeader>
<MudCardActions>
<MudButton Variant="Variant.Filled" Color ="Color.Primary" OnClick="(()=>DeleteFromParse(index))"> Delete </MudButton>
</MudCardActions>
</MudCardHeader>
<MudCardContent>
<MudTextField Class="ma-3" AutoGrow="true" @bind-Value="ParseResult[index]"></MudTextField>
</MudCardContent>
</MudCard>
} }
<MudText>@Error</MudText> <MudText>@Error</MudText>
</MudPaper> </MudPaper>

View File

@@ -1,6 +1,7 @@
using Blazored.TextEditor; using Blazored.TextEditor;
using Entities.Contracts; using Entities.Contracts;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MudBlazor; using MudBlazor;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using TechHelper.Client.AI; using TechHelper.Client.AI;
@@ -62,7 +63,7 @@ namespace TechHelper.Client.Pages.Editor
ProgStatues = ProgEnum.AIPrase.ToString(); ProgStatues = ProgEnum.AIPrase.ToString();
ProgStatues = $"<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>ȴ<EFBFBD>"; ProgStatues = $"<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>ȴ<EFBFBD>";
Snackbar.Add("<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>ȴ<EFBFBD>"); Snackbar.Add("<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>ȴ<EFBFBD>", Severity.Info);
StateHasChanged(); StateHasChanged();
string respon = await aIService.CallGLM(QuillHTMLContent, AIConfiguration.BreakQuestions); string respon = await aIService.CallGLM(QuillHTMLContent, AIConfiguration.BreakQuestions);
if (respon == null) if (respon == null)
@@ -81,18 +82,18 @@ namespace TechHelper.Client.Pages.Editor
foreach (var item in ParRespon.Items) foreach (var item in ParRespon.Items)
{ {
ProgStatues = $"<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD>{i}<7D><>, <20><><EFBFBD>ȴ<EFBFBD>"; ProgStatues = $"<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD>{i}<7D><>, <20><><EFBFBD>ȴ<EFBFBD>";
Snackbar.Add($"<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD>{i}<7D><>, <20><><EFBFBD>ȴ<EFBFBD>"); Snackbar.Add($"<22><><EFBFBD>ڽ<EFBFBD><DABD><EFBFBD><EFBFBD><EFBFBD>{i}<7D><>, <20><><EFBFBD>ȴ<EFBFBD>", Severity.Info);
StateHasChanged(); StateHasChanged();
i++; i++;
try try
{ {
var parResult = await aIService.CallGLM(item, AIConfiguration.ParseSignelQuestion); var parResult = await aIService.CallGLM(item, AIConfiguration.ParseSignelQuestion2);
ParseResult.Add(parResult); ParseResult.Add(parResult);
} }
catch (Exception ex) catch (Exception ex)
{ {
Snackbar.Add($"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>{i}<7D><>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD>Ժ<EFBFBD><D4BA><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD>Ϊ:{ex.Message}"); Snackbar.Add($"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>{i}<7D><>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD>Ժ<EFBFBD><D4BA><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD>Ϊ:{ex.Message}", Severity.Error);
} }
} }
} }
@@ -100,6 +101,7 @@ namespace TechHelper.Client.Pages.Editor
AIParseResult = respon; AIParseResult = respon;
ProgStatues = ProgEnum.AIRectify.ToString(); ProgStatues = ProgEnum.AIRectify.ToString();
Snackbar.Add($"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>", Severity.Info);
//await ReCorrectXMLAsync(); //await ReCorrectXMLAsync();
@@ -124,12 +126,12 @@ namespace TechHelper.Client.Pages.Editor
} }
catch (Exception ex) catch (Exception ex)
{ {
Snackbar.Add("<22><><EFBFBD>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>°<EFBFBD>" + ex.Message); Snackbar.Add("<22><><EFBFBD>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>°<EFBFBD>" + ex.Message, Severity.Error);
} }
if (string.IsNullOrEmpty(respon)) if (string.IsNullOrEmpty(respon))
{ {
lode = false; lode = false;
Snackbar.Add("<22><><EFBFBD>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>°<EFBFBD>"); Snackbar.Add("<22><><EFBFBD>˵<EFBFBD><CBB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>°<EFBFBD>", Severity.Error);
} }
@@ -146,16 +148,58 @@ namespace TechHelper.Client.Pages.Editor
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
Snackbar.Add("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" + ex.Message); Snackbar.Add("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" + ex.Message, Severity.Error);
} }
catch (Exception ex) catch (Exception ex)
{ {
Snackbar.Add("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" + ex.Message); Snackbar.Add("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>" + ex.Message, Severity.Error);
Error = ex.Message; Error = ex.Message;
} }
} }
private void DeleteFromParse(int index)
{
if (index >= 0 && index < ParseResult.Count)
{
ParseResult.RemoveAt(index);
StateHasChanged();
}
}
[Inject]
public IJSRuntime JSRuntime { get; set; }
private IJSObjectReference jSObjectReference { get; set; }
protected override async Task OnInitializedAsync()
{
jSObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("import",
"./scripts/jsTools.js");
}
private async Task CopyToClipboard()
{
try
{
// <20><><EFBFBD><EFBFBD> JavaScript <20><><EFBFBD><EFBFBD>
bool success = await jSObjectReference.InvokeAsync<bool>("copyTextToClipboard", AIConfiguration.ParseSignelQuestion2);
if (success)
{
Snackbar.Add("<22>ı<EFBFBD><C4B1>ѳɹ<D1B3><C9B9><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>壡");
}
else
{
Snackbar.Add("<22><><EFBFBD><EFBFBD><EFBFBD>ı<EFBFBD><C4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD>ܡ<EFBFBD>");
}
}
catch (Exception ex)
{
Snackbar.Add($"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: {ex.Message}");
Console.WriteLine($"<22><><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>: {ex.Message}");
}
}
private void ParseQuestions() private void ParseQuestions()
{ {
ParsedQuestions = new List<ParsedQuestion>(); ParsedQuestions = new List<ParsedQuestion>();

View File

@@ -8,10 +8,6 @@
<MudStack Row="true" AlignItems="AlignItems.Center"> <MudStack Row="true" AlignItems="AlignItems.Center">
<MudText Typo="@(IsNested ? Typo.h6 : Typo.h5)">@QuestionGroup.Id. </MudText> @* 嵌套时字号稍小 *@ <MudText Typo="@(IsNested ? Typo.h6 : Typo.h5)">@QuestionGroup.Id. </MudText> @* 嵌套时字号稍小 *@
<MudText Typo="@(IsNested ? Typo.h6 : Typo.h5)">@QuestionGroup.Title</MudText> <MudText Typo="@(IsNested ? Typo.h6 : Typo.h5)">@QuestionGroup.Title</MudText>
@if (!string.IsNullOrEmpty(QuestionGroup.ScoreProblemMarker))
{
<MudText Typo="Typo.caption" Color="Color.Warning" Class="ml-2">( @QuestionGroup.ScoreProblemMarker )</MudText>
}
</MudStack> </MudStack>
@if (!string.IsNullOrEmpty(QuestionGroup.QuestionReference)) @if (!string.IsNullOrEmpty(QuestionGroup.QuestionReference))
{ {
@@ -32,10 +28,6 @@
<MudStack Row="true" AlignItems="AlignItems.Baseline" Class="mb-2"> <MudStack Row="true" AlignItems="AlignItems.Baseline" Class="mb-2">
<MudText Typo="Typo.body1">@qitem.SubId. </MudText> <MudText Typo="Typo.body1">@qitem.SubId. </MudText>
<MudText Typo="Typo.body1">@qitem.Stem</MudText> <MudText Typo="Typo.body1">@qitem.Stem</MudText>
@if (!string.IsNullOrEmpty(qitem.ScoreProblemMarker))
{
<MudText Typo="Typo.caption" Color="Color.Warning" Class="ml-1">( @qitem.ScoreProblemMarker )</MudText>
}
</MudStack> </MudStack>
@if (!string.IsNullOrEmpty(qitem.SampleAnswer)) @if (!string.IsNullOrEmpty(qitem.SampleAnswer))
{ {

View File

@@ -29,6 +29,7 @@
<a href="" class="reload">Reload</a> <a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a> <a class="dismiss">🗙</a>
</div> </div>
<script src="scripts/jsTools.js"></script>
<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script> <script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
<script src="_framework/blazor.webassembly.js"></script> <script src="_framework/blazor.webassembly.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script> <script src="_content/MudBlazor/MudBlazor.min.js"></script>
@@ -37,4 +38,5 @@
<script src="_content/Blazored.TextEditor/Blazored-BlazorQuill.js"></script> <script src="_content/Blazored.TextEditor/Blazored-BlazorQuill.js"></script>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,15 @@

export async function copyTextToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch (err) {
console.error('复制失败:', err);
return false;
}
}
export function show(message) {
alert(message);
}