|
|
@@ -1,4 +1,4 @@
|
|
|
-/****************************************************************************
|
|
|
+/****************************************************************************
|
|
|
* 2023.11 DESKTOP-DL7CJI0
|
|
|
****************************************************************************/
|
|
|
|
|
|
@@ -8,23 +8,29 @@ using UnityEngine;
|
|
|
using UnityEngine.UI;
|
|
|
using QFramework;
|
|
|
using ChivaXR.Op;
|
|
|
+using iTextSharp.text.pdf;
|
|
|
+using iTextSharp.text;
|
|
|
+using System.IO;
|
|
|
+using PdfFont = iTextSharp.text.Font;
|
|
|
|
|
|
namespace QFramework
|
|
|
{
|
|
|
public partial class ScoreInfo : UIElement
|
|
|
- {
|
|
|
- void Start()
|
|
|
+ {
|
|
|
+ void Start()
|
|
|
{
|
|
|
ConfirmBtn.gameObject.SetActive(true);
|
|
|
|
|
|
ConfirmBtn.onClick.AddListener(OnConfirmBtnClick);
|
|
|
+
|
|
|
+ ExportBtn.onClick.AddListener(OnExportPDFClick);
|
|
|
}
|
|
|
|
|
|
public void SetInfo(List<ExamProcessElement> elements, float score, int useTime)
|
|
|
{
|
|
|
int remain = useTime % 60;
|
|
|
|
|
|
- UseTime.text = (useTime/60).ToString() + "·ÖÖÓ" + remain + "Ãë";
|
|
|
+ UseTime.text = (useTime/60).ToString() + "分钟" + remain + "秒";
|
|
|
Score.text = score.ToString();
|
|
|
Score_Shadow.text = score.ToString();
|
|
|
|
|
|
@@ -42,20 +48,353 @@ namespace QFramework
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void Update()
|
|
|
- {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
public void OnConfirmBtnClick()
|
|
|
{
|
|
|
//UIKit.GetPanel<PC_OperatePanel>().ScoreInfo.gameObject.SetActive(false);
|
|
|
- //¹Ø±Õ²Ëµ¥Í˳öϵͳ
|
|
|
+ //å…³é—è�œå�•退出系统
|
|
|
Application.Quit();
|
|
|
}
|
|
|
|
|
|
protected override void OnBeforeDestroy()
|
|
|
{
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+ public void OnExportPDFClick()
|
|
|
+ {
|
|
|
+ UserProxy tmpUserProxy = DAL.Instance.Get<UserProxy>();
|
|
|
+ ExamProxy tmpExamProxy = DAL.Instance.Get<ExamProxy>();
|
|
|
+ ScoreInfo scorePanel = UIKit.GetPanel<PC_OperatePanel>().ScoreInfo;
|
|
|
+
|
|
|
+ if (scorePanel == null)
|
|
|
+ {
|
|
|
+ Debug.LogError("â�Œ 未找到 ScoreInfo é�¢æ�¿ï¼Œè¯·ç¡®ä¿�在当å‰�场景ä¸å˜åœ¨ã€‚");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== PDF导出路径 ======
|
|
|
+ //选择的路径--如果没有选择路径那就使用默认路径
|
|
|
+ string tmpSelectSavePath = FolderBrowserHelper.GetPathFromWindowsExplorer("请选择�绩导出�置");
|
|
|
+
|
|
|
+ string tmpSavePath = string.Empty;
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(tmpSelectSavePath))
|
|
|
+ {
|
|
|
+ tmpSelectSavePath = Path.Combine(Application.streamingAssetsPath, "考试�绩");
|
|
|
+
|
|
|
+ tmpSavePath = Path.Combine(Application.streamingAssetsPath + "/考试�绩", $"{tmpUserProxy.userInfo.userName}_{DateTime.Now:yyyy年MM月dd日HH时mm分}_�绩�.pdf");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ tmpSavePath = Path.Combine(tmpSelectSavePath, $"{tmpUserProxy.userInfo.userName}_{DateTime.Now:yyyy年MM月dd日HH时mm分}_�绩�.pdf");
|
|
|
+ }
|
|
|
+
|
|
|
+ Document doc = new Document(PageSize.A4, 60, 60, 80, 60); //上边�略大,留页眉空间
|
|
|
+ PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(tmpSavePath, FileMode.CreateNew));
|
|
|
+
|
|
|
+ // ===== 页脚和页ç � =====
|
|
|
+ writer.PageEvent = new PdfPageEvents();
|
|
|
+
|
|
|
+ doc.Open();
|
|
|
+
|
|
|
+ // ====== å—体设置 ======
|
|
|
+ string fontPath = Path.Combine(Application.streamingAssetsPath, "Fonts/æˆ�绩å—体.ttf");
|
|
|
+ if (!File.Exists(fontPath))
|
|
|
+ {
|
|
|
+ Debug.LogError("å—体文件未找到:" + fontPath);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ BaseFont bfChinese = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
|
|
|
+ PdfFont smallFont = new PdfFont(bfChinese, 10) { Color = new BaseColor(100, 100, 100) }; // �色
|
|
|
+ PdfFont normalFont = new PdfFont(bfChinese, 12);
|
|
|
+ PdfFont boldFont = new PdfFont(bfChinese, 12, PdfFont.BOLD);
|
|
|
+ PdfFont titleFont = new PdfFont(bfChinese, 30, PdfFont.BOLD);
|
|
|
+
|
|
|
+ PdfContentByte cb = writer.DirectContent;
|
|
|
+
|
|
|
+ // ===== 横线 =====
|
|
|
+ float lineY = doc.Top - 20f;
|
|
|
+ cb.MoveTo(doc.Left, lineY);
|
|
|
+ cb.LineTo(doc.Right, lineY);
|
|
|
+ cb.Stroke();
|
|
|
+
|
|
|
+ // ===== 页眉在横线之上 =====
|
|
|
+ string headerText = "虚拟仿真实验ä¸å¿ƒ";
|
|
|
+ ColumnText.ShowTextAligned(
|
|
|
+ cb,
|
|
|
+ Element.ALIGN_CENTER,
|
|
|
+ new Phrase(headerText, smallFont),
|
|
|
+ (doc.Left + doc.Right) / 2,
|
|
|
+ lineY + 10, // 横线上方 10pt
|
|
|
+ 0
|
|
|
+ );
|
|
|
+
|
|
|
+ // ===== LOGO 横线上方左侧 =====
|
|
|
+ string logoPath = Path.Combine(Application.streamingAssetsPath, "Logos", "�绩logo.png");
|
|
|
+
|
|
|
+ // 统一去除 URL �缀(特别是 file://)
|
|
|
+ if (logoPath.StartsWith("file://"))
|
|
|
+ logoPath = logoPath.Replace("file://", "");
|
|
|
+
|
|
|
+ if (!File.Exists(logoPath))
|
|
|
+ {
|
|
|
+ // Unity Editor 下 StreamingAssets 在项目路径内
|
|
|
+ logoPath = Path.Combine(Application.streamingAssetsPath, "Logos", "�绩logo.png");
|
|
|
+ }
|
|
|
+ float logoBottomY = lineY; // ç”¨äºŽè®¡ç®—æ ‡é¢˜ä½�ç½®
|
|
|
+ if (File.Exists(logoPath))
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ byte[] logoBytes = File.ReadAllBytes(logoPath);
|
|
|
+ iTextSharp.text.Image logo = iTextSharp.text.Image.GetInstance(logoBytes);
|
|
|
+
|
|
|
+ // 👉 �固定高度,让宽度按比例缩放
|
|
|
+ float targetHeight = 50f; // 设定统一显示宽度
|
|
|
+ float scaleFactor = targetHeight / logo.Height; // 缩放比例
|
|
|
+ logo.ScalePercent(scaleFactor * 100); // iTextSharp 用百分比
|
|
|
+
|
|
|
+ // ��顶部�离横线 5pt
|
|
|
+ float logoTopY = lineY + 5f + logo.ScaledHeight;
|
|
|
+ float logoPosY = logoTopY - logo.ScaledHeight;
|
|
|
+ logoBottomY = logoPosY;
|
|
|
+
|
|
|
+ // 让左下角对�页边�
|
|
|
+ logo.SetAbsolutePosition(doc.Left, logoPosY);
|
|
|
+ doc.Add(logo);
|
|
|
+
|
|
|
+ Debug.Log($"✅ Logo æ·»åŠ æˆ�功(原始大å°�: {logo.Width}x{logo.Height}, 缩放比例: {scaleFactor:F2})");
|
|
|
+ }
|
|
|
+ catch (System.Exception e)
|
|
|
+ {
|
|
|
+ Debug.LogError("� 读� Logo 出错:" + e.Message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.LogWarning("⚠� 未找到 Logo 文件:" + logoPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ===== ä¸»æ ‡é¢˜ï¼ˆå�•独一行居ä¸ï¼‰ =====
|
|
|
+ Paragraph title = new Paragraph("� 绩 �", titleFont);
|
|
|
+ title.Alignment = Element.ALIGN_CENTER;
|
|
|
+ title.SpacingBefore = 30f;
|
|
|
+ title.SpacingAfter = 20f;
|
|
|
+ doc.Add(title);
|
|
|
+
|
|
|
+ // ===== 基本信æ�¯è¡¨æ ¼ =====
|
|
|
+ PdfPTable infoTable = new PdfPTable(4);
|
|
|
+ infoTable.WidthPercentage = 100;
|
|
|
+ infoTable.SpacingBefore = 10f;
|
|
|
+ infoTable.SpacingAfter = 15f;
|
|
|
+ infoTable.SetWidths(new float[] { 1.2f, 2f, 1.2f, 2f });
|
|
|
+
|
|
|
+ AddCell(infoTable, "姓�", boldFont, new BaseColor(235, 235, 235));
|
|
|
+ AddCell(infoTable, tmpUserProxy.userInfo.userName, normalFont);
|
|
|
+ AddCell(infoTable, "实验日期", boldFont, new BaseColor(235, 235, 235));
|
|
|
+ AddCell(infoTable, DateTime.Now.ToString("yyyy年MM月dd日HH时"), normalFont);
|
|
|
+
|
|
|
+ AddCell(infoTable, "实验�称", boldFont, new BaseColor(235, 235, 235));
|
|
|
+ AddCell(infoTable, OperateSetting.Instance.m_CourseName, normalFont);
|
|
|
+ AddCell(infoTable, "总�绩", boldFont, new BaseColor(235, 235, 235));
|
|
|
+ AddCell(infoTable, float.Parse(scorePanel.Score.text).ToString("F1") + " 分", normalFont);
|
|
|
+
|
|
|
+ AddCell(infoTable, "用时", boldFont, new BaseColor(235, 235, 235));
|
|
|
+ AddCell(infoTable, scorePanel.UseTime.text, normalFont);
|
|
|
+ AddCell(infoTable, "", normalFont);
|
|
|
+ AddCell(infoTable, "", normalFont);
|
|
|
+
|
|
|
+ doc.Add(infoTable);
|
|
|
+
|
|
|
+ // ===== æ“�作æ¥éª¤è¡¨æ ¼ =====
|
|
|
+ PdfPTable stepTable = new PdfPTable(3);
|
|
|
+ stepTable.WidthPercentage = 100;
|
|
|
+ stepTable.SetWidths(new float[] { 1f, 3f, 1f });
|
|
|
+
|
|
|
+ AddHeaderCell(stepTable, "��", boldFont);
|
|
|
+ AddHeaderCell(stepTable, "æ“�作æ¥éª¤", boldFont);
|
|
|
+ AddHeaderCell(stepTable, "分数", boldFont);
|
|
|
+
|
|
|
+ foreach (var examInfo in tmpExamProxy.examScoreInfos)
|
|
|
+ {
|
|
|
+ AddCell(stepTable, examInfo.id.ToString(), normalFont);
|
|
|
+ AddCell(stepTable, examInfo.stepName, normalFont);
|
|
|
+
|
|
|
+ string score = examInfo.score < 0 ? "0" : examInfo.score.ToString("F1");
|
|
|
+
|
|
|
+ AddCell(stepTable, score.ToString(), normalFont);
|
|
|
+ }
|
|
|
+
|
|
|
+ doc.Add(stepTable);
|
|
|
+
|
|
|
+ // ===== è¯„è¯ =====
|
|
|
+ doc.Add(new Paragraph("\n"));
|
|
|
+ string commentText = GenerateComment(float.Parse(scorePanel.Score.text));
|
|
|
+ Paragraph comment = new Paragraph($"评è¯ï¼š{commentText}", normalFont);
|
|
|
+ comment.SpacingBefore = 10;
|
|
|
+ doc.Add(comment);
|
|
|
+
|
|
|
+ doc.Close();
|
|
|
+ writer.Close();
|
|
|
+ Debug.Log($"✅ �绩�导出�功:{tmpSavePath}");
|
|
|
+
|
|
|
+ CheckGradeCount(tmpSelectSavePath,20, "*�绩�.pdf");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 生æˆ�评è¯
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="score"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private string GenerateComment(float score)
|
|
|
+ {
|
|
|
+ if (score >= 90f)
|
|
|
+ return "表现优秀,�作熟练,�解深入。";
|
|
|
+ else if (score >= 75f)
|
|
|
+ return "完æˆ�良好,掌æ�¡è¾ƒä¸ºæ‰Žå®žï¼Œå�¯ç»§ç»å·©å›ºã€‚";
|
|
|
+ else if (score >= 60f)
|
|
|
+ return "åŸºæœ¬è¾¾æ ‡ï¼Œä½†ä»�éœ€åŠ å¼ºç�†è§£ä¸Žæ“�作细节。";
|
|
|
+ else
|
|
|
+ return "需改进,建议å¤�ä¹ å®žéªŒæ¥éª¤å¹¶æ��高熟练度。";
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddHeaderCell(PdfPTable table, string text, PdfFont font)
|
|
|
+ {
|
|
|
+ PdfPCell cell = new PdfPCell(new Phrase(text, font));
|
|
|
+ cell.BackgroundColor = new BaseColor(220, 220, 220);
|
|
|
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
|
|
|
+ cell.MinimumHeight = 25;
|
|
|
+ table.AddCell(cell);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddCell(PdfPTable table, string text, PdfFont font, BaseColor bg = null)
|
|
|
+ {
|
|
|
+ PdfPCell cell = new PdfPCell(new Phrase(text, font));
|
|
|
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
|
|
|
+ cell.VerticalAlignment = Element.ALIGN_MIDDLE;
|
|
|
+ cell.MinimumHeight = 22;
|
|
|
+ if (bg != null) cell.BackgroundColor = bg;
|
|
|
+ table.AddCell(cell);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ===== 页脚页ç �事件类 =====
|
|
|
+ private class PdfPageEvents : PdfPageEventHelper
|
|
|
+ {
|
|
|
+ private PdfTemplate totalPages;
|
|
|
+ private BaseFont baseFont;
|
|
|
+ private PdfFont pdfFont;
|
|
|
+ public override void OnOpenDocument(PdfWriter writer, Document document)
|
|
|
+ {
|
|
|
+ string fontPath = Path.Combine(Application.streamingAssetsPath, "Fonts/æˆ�绩å—体.ttf");
|
|
|
+
|
|
|
+ baseFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
|
|
|
+ pdfFont = new PdfFont(baseFont,10);
|
|
|
+
|
|
|
+ totalPages = writer.DirectContent.CreateTemplate(100, 100);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void OnEndPage(PdfWriter writer, Document document)
|
|
|
+ {
|
|
|
+ base.OnEndPage(writer, document);
|
|
|
+
|
|
|
+ // æ·»åŠ é¡µçœ‰
|
|
|
+ //AddHeader(writer, document);
|
|
|
+
|
|
|
+ // æ·»åŠ é¡µè„š
|
|
|
+ AddFooter(writer, document);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddHeader(PdfWriter writer, Document document)
|
|
|
+ {
|
|
|
+ PdfPTable headerTable = new PdfPTable(1);
|
|
|
+ headerTable.TotalWidth = document.PageSize.Width - document.LeftMargin - document.RightMargin;
|
|
|
+
|
|
|
+ PdfPCell cell = new PdfPCell(new Phrase("这是页眉内容", pdfFont));
|
|
|
+ cell.Border = Rectangle.BOTTOM_BORDER;
|
|
|
+ cell.BorderColor = BaseColor.GRAY;
|
|
|
+ cell.BorderWidth = 1f;
|
|
|
+ cell.PaddingBottom = 5f;
|
|
|
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
|
|
|
+
|
|
|
+ headerTable.AddCell(cell);
|
|
|
+ headerTable.WriteSelectedRows(0, -1, document.LeftMargin, document.PageSize.Height - document.TopMargin + 10,
|
|
|
+ writer.DirectContent);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddFooter(PdfWriter writer, Document document)
|
|
|
+ {
|
|
|
+ PdfPTable footerTable = new PdfPTable(2);
|
|
|
+ footerTable.TotalWidth = document.PageSize.Width - document.LeftMargin - document.RightMargin;
|
|
|
+
|
|
|
+ // 左侧文本
|
|
|
+ PdfPCell leftCell = new PdfPCell(new Phrase("è™šæ‹Ÿä»¿çœŸå®žéªŒè€ƒæ ¸ç³»ç»Ÿ © 2025", pdfFont));
|
|
|
+ leftCell.Border = Rectangle.TOP_BORDER;
|
|
|
+ leftCell.BorderColor = BaseColor.GRAY;
|
|
|
+ leftCell.HorizontalAlignment = Element.ALIGN_LEFT;
|
|
|
+
|
|
|
+ // å±…ä¸é¡µç �
|
|
|
+ string pageText = $"第 {writer.PageNumber} 页";
|
|
|
+ PdfPCell centerCell = new PdfPCell(new Phrase(pageText, pdfFont));
|
|
|
+ centerCell.Border = Rectangle.TOP_BORDER;
|
|
|
+ centerCell.BorderColor = BaseColor.GRAY;
|
|
|
+ centerCell.HorizontalAlignment = Element.ALIGN_RIGHT;
|
|
|
+
|
|
|
+ // �侧总页数
|
|
|
+ //PdfPCell rightCell = new PdfPCell(new Phrase("总页数:", pdfFont));
|
|
|
+ //rightCell.Border = Rectangle.TOP_BORDER;
|
|
|
+ //rightCell.BorderColor = BaseColor.GRAY;
|
|
|
+ //rightCell.HorizontalAlignment = Element.ALIGN_RIGHT;
|
|
|
+
|
|
|
+ footerTable.AddCell(leftCell);
|
|
|
+ footerTable.AddCell(centerCell);
|
|
|
+ //footerTable.AddCell(rightCell);
|
|
|
+
|
|
|
+ footerTable.WriteSelectedRows(0, -1, document.LeftMargin, document.BottomMargin - 10,
|
|
|
+ writer.DirectContent);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void OnCloseDocument(PdfWriter writer, Document document)
|
|
|
+ {
|
|
|
+ base.OnCloseDocument(writer, document);
|
|
|
+
|
|
|
+ // åœ¨æ–‡æ¡£å…³é—æ—¶å†™å…¥æ€»é¡µæ•°
|
|
|
+ //ColumnText.ShowTextAligned(totalPages, Element.ALIGN_LEFT,
|
|
|
+ // new Phrase((writer.PageNumber - 1).ToString(), pdfFont),
|
|
|
+ // 2, 2, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查�绩数�
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="path">路径</param>
|
|
|
+ /// <param name="quantityLimit">�制数�</param>
|
|
|
+ /// <param name="searchPattern">�索�件</param>
|
|
|
+ private void CheckGradeCount(string path, int quantityLimit, string searchPattern)
|
|
|
+ {
|
|
|
+ string[] filePaths = FileToolkit.SearchFilePaths(path, searchPattern);
|
|
|
+
|
|
|
+ if (filePaths.Length > quantityLimit)
|
|
|
+ {
|
|
|
+ for (int i = 0; i < filePaths.Length - 1; i++)
|
|
|
+ {
|
|
|
+ for (int j = 0; j < filePaths.Length - i - 1; j++)
|
|
|
+ {
|
|
|
+ if (FileToolkit.CompareBeforAndAfterCreateTime(filePaths[j], filePaths[j + 1]))
|
|
|
+ {
|
|
|
+ // 交� arr[j] 和 arr[j + 1]
|
|
|
+ string temp = filePaths[j];
|
|
|
+ filePaths[j] = filePaths[j + 1];
|
|
|
+ filePaths[j + 1] = temp;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Debug.Log("åˆ é™¤æˆ�绩" + filePaths[0]);
|
|
|
+ File.Delete(filePaths[0]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
}
|