1090 lines
35 KiB
C#
1090 lines
35 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
|
||
/// <summary>
|
||
/// BFI历史记录和数据导出UI面板
|
||
/// 提供历史记录查看、搜索、选择和导出功能
|
||
/// </summary>
|
||
public class BFIHistoryExportPanel : BasePanel
|
||
{
|
||
[Header("UI引用")]
|
||
[SerializeField] private Transform historyListParent;
|
||
[SerializeField] private GameObject historyItemPrefab;
|
||
[SerializeField] private ScrollRect historyScrollRect;
|
||
[SerializeField] private TMP_InputField searchInputField;
|
||
[SerializeField] private KeyboardLayout searchKeyboardLayout = KeyboardLayout.Pinyin;
|
||
[SerializeField] private Button searchButton;
|
||
[SerializeField] private Button clearRecordButton;
|
||
|
||
[Header("过滤器")]
|
||
[SerializeField] private TMP_Dropdown dateFilterDropdown;
|
||
[SerializeField] private TMP_InputField startDateInput;
|
||
[SerializeField] private TMP_InputField endDateInput;
|
||
[SerializeField] private Button applyDateFilterButton;
|
||
|
||
[Header("选择控制")]
|
||
[SerializeField] private Toggle selectAllToggle;
|
||
// [SerializeField] private Button selectTodayButton;
|
||
// [SerializeField] private Button selectWeekButton;
|
||
// [SerializeField] private Button clearSelectionButton;
|
||
[SerializeField] private TextMeshProUGUI selectionCountText;
|
||
|
||
[Header("导出控制")]
|
||
[SerializeField] private TMP_Dropdown exportFormatDropdown;
|
||
[SerializeField] private Button exportSelectedButton;
|
||
[SerializeField] private Button exportAllButton;
|
||
[SerializeField] private Button previewButton;
|
||
|
||
[Header("通用按钮")]
|
||
public Button HomeButton;
|
||
public Button BackButton;
|
||
|
||
[Header("统计信息")]
|
||
[SerializeField] private TextMeshProUGUI totalRecordsText;
|
||
[SerializeField] private TextMeshProUGUI totalTestTimeText;
|
||
[SerializeField] private TextMeshProUGUI todayRecordsText;
|
||
[SerializeField] private TextMeshProUGUI weekRecordsText;
|
||
[SerializeField] private TextMeshProUGUI monthRecordsText;
|
||
|
||
[Header("状态显示")]
|
||
[SerializeField] private TextMeshProUGUI statusText;
|
||
// [SerializeField] private Slider progressSlider;
|
||
// [SerializeField] private GameObject loadingPanel;
|
||
|
||
private List<BFITestRecord> allRecords;
|
||
private List<BFITestRecord> filteredRecords;
|
||
private List<BFIHistoryItem> historyItems;
|
||
private Dictionary<string, BFIHistoryItem> historyItemsByRecordId;
|
||
private HashSet<string> selectedRecordIds;
|
||
private string lastExportedFilePath;
|
||
private string lastExportFailureMessage;
|
||
private Coroutine historyListBuildCoroutine;
|
||
private const int HISTORY_ITEMS_PER_FRAME = 20;
|
||
|
||
public override void Init()
|
||
{
|
||
allRecords = new List<BFITestRecord>();
|
||
filteredRecords = new List<BFITestRecord>();
|
||
historyItems = new List<BFIHistoryItem>();
|
||
historyItemsByRecordId = new Dictionary<string, BFIHistoryItem>();
|
||
selectedRecordIds = new HashSet<string>();
|
||
|
||
InitializeUI();
|
||
LoadHistoryRecords();
|
||
UpdateStatistics();
|
||
|
||
// 订阅历史管理器事件
|
||
if (BFIHistoryManager.Instance != null)
|
||
{
|
||
BFIHistoryManager.Instance.OnRecordAdded += OnRecordAdded;
|
||
BFIHistoryManager.Instance.OnRecordUpdated += OnRecordUpdated;
|
||
BFIHistoryManager.Instance.OnRecordRemoved += OnRecordRemoved;
|
||
BFIHistoryManager.Instance.OnHistoryLoaded += OnHistoryLoaded;
|
||
}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
StopHistoryListBuildCoroutine();
|
||
HideCustomKeyboardIfNeeded();
|
||
|
||
// 取消订阅事件
|
||
if (BFIHistoryManager.Instance != null)
|
||
{
|
||
BFIHistoryManager.Instance.OnRecordAdded -= OnRecordAdded;
|
||
BFIHistoryManager.Instance.OnRecordUpdated -= OnRecordUpdated;
|
||
BFIHistoryManager.Instance.OnRecordRemoved -= OnRecordRemoved;
|
||
BFIHistoryManager.Instance.OnHistoryLoaded -= OnHistoryLoaded;
|
||
}
|
||
}
|
||
|
||
private void InitializeUI()
|
||
{
|
||
// 搜索功能
|
||
if (searchButton != null)
|
||
searchButton.onClick.AddListener(OnSearchClicked);
|
||
|
||
if (searchInputField != null)
|
||
{
|
||
searchInputField.onEndEdit.AddListener(OnSearchInputChanged);
|
||
SetupSearchCustomKeyboard();
|
||
}
|
||
|
||
// 日期过滤
|
||
if (applyDateFilterButton != null)
|
||
applyDateFilterButton.onClick.AddListener(OnApplyDateFilter);
|
||
|
||
// 选择控制
|
||
if (selectAllToggle != null){
|
||
selectAllToggle.onValueChanged.AddListener(OnSelectAllToggled);
|
||
selectAllToggle.isOn = false;
|
||
}
|
||
|
||
// 清空记录
|
||
if (clearRecordButton != null)
|
||
clearRecordButton.onClick.AddListener(OnClearClicked);
|
||
|
||
// 导出控制
|
||
if (exportSelectedButton != null)
|
||
exportSelectedButton.onClick.AddListener(OnExportSelectedClicked);
|
||
if (exportAllButton != null)
|
||
exportAllButton.onClick.AddListener(OnExportAllClicked);
|
||
if (previewButton != null)
|
||
previewButton.onClick.AddListener(OnPreviewClicked);
|
||
|
||
// 通用按钮
|
||
if (HomeButton != null)
|
||
HomeButton.onClick.AddListener(() =>
|
||
{
|
||
HideCustomKeyboardIfNeeded();
|
||
ReturnToHome();
|
||
});
|
||
if (BackButton != null)
|
||
BackButton.onClick.AddListener(() =>
|
||
{
|
||
HideCustomKeyboardIfNeeded();
|
||
ClosePanel();
|
||
});
|
||
|
||
// 初始化下拉菜单
|
||
InitializeDropdowns();
|
||
|
||
// 设置默认日期
|
||
if (startDateInput != null)
|
||
startDateInput.text = DateTime.Today.AddDays(-7).ToString("yyyy-MM-dd");
|
||
if (endDateInput != null)
|
||
endDateInput.text = DateTime.Today.ToString("yyyy-MM-dd");
|
||
}
|
||
|
||
private void InitializeDropdowns()
|
||
{
|
||
// 日期过滤下拉菜单
|
||
if (dateFilterDropdown != null)
|
||
{
|
||
dateFilterDropdown.ClearOptions();
|
||
dateFilterDropdown.AddOptions(new List<string>
|
||
{
|
||
"全部时间",
|
||
"今天",
|
||
"本周",
|
||
"本月",
|
||
// "自定义日期范围"
|
||
});
|
||
dateFilterDropdown.SetValueWithoutNotify(0);
|
||
dateFilterDropdown.RefreshShownValue();
|
||
}
|
||
|
||
// 导出格式下拉菜单
|
||
if (exportFormatDropdown != null)
|
||
{
|
||
exportFormatDropdown.ClearOptions();
|
||
exportFormatDropdown.AddOptions(new List<string>
|
||
{
|
||
"文本报告",
|
||
"CSV数据",
|
||
"PDF报告",
|
||
});
|
||
}
|
||
}
|
||
|
||
private void SetupSearchCustomKeyboard()
|
||
{
|
||
if (searchInputField == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (CustomKeyboardManager.Instance == null)
|
||
{
|
||
Debug.LogWarning("BFI导出搜索框未找到CustomKeyboardManager,保留默认输入方式");
|
||
return;
|
||
}
|
||
|
||
CustomKeyboardManager.SetupInputField(searchInputField, searchKeyboardLayout,
|
||
(newValue) =>
|
||
{
|
||
searchInputField.text = newValue;
|
||
ApplySearch();
|
||
},
|
||
clearOnFirstInput: false);
|
||
}
|
||
|
||
private void HideCustomKeyboardIfNeeded()
|
||
{
|
||
if (CustomKeyboardManager.Instance != null && CustomKeyboardManager.Instance.IsKeyboardShowing())
|
||
{
|
||
CustomKeyboardManager.Instance.HideKeyboard();
|
||
}
|
||
}
|
||
|
||
private List<BFITestRecord> SortRecordsByRecentFirst(IEnumerable<BFITestRecord> records)
|
||
{
|
||
return (records ?? Enumerable.Empty<BFITestRecord>())
|
||
.Where(record => record != null)
|
||
.OrderByDescending(record => record.StartTime)
|
||
.ThenByDescending(record => record.EndTime)
|
||
.ToList();
|
||
}
|
||
|
||
private void LoadHistoryRecords()
|
||
{
|
||
try
|
||
{
|
||
UpdateStatus("正在加载历史记录...");
|
||
|
||
RecoverPendingRecordingJournal();
|
||
FlushCurrentRecordingSnapshot();
|
||
|
||
if (BFIHistoryManager.Instance != null)
|
||
{
|
||
allRecords = BFIHistoryManager.Instance.LoadHistoryRecordSummaries(false);
|
||
filteredRecords = SortRecordsByRecentFirst(allRecords);
|
||
|
||
RefreshHistoryList();
|
||
UpdateSelectionCount();
|
||
|
||
string historyPath = BFIHistoryManager.Instance.GetHistoryFilePath();
|
||
UpdateStatus(allRecords.Count > 0
|
||
? $"已加载 {allRecords.Count} 条历史记录"
|
||
: $"未找到已保存的BFI历史记录");
|
||
Debug.Log($"BFI历史索引路径: {historyPath}");
|
||
UpdateStatistics();
|
||
}
|
||
else
|
||
{
|
||
UpdateStatus("历史管理器未初始化");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"加载历史记录失败: {ex.Message}");
|
||
UpdateStatus($"加载失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void FlushCurrentRecordingSnapshot()
|
||
{
|
||
try
|
||
{
|
||
var dataService = ServiceLocator.IsRegistered<IDataService>() ? ServiceLocator.Get<IDataService>() as DataService : null;
|
||
if (dataService != null)
|
||
{
|
||
dataService.FlushCurrentBFIRecordingSnapshot("打开BFI数据导出界面");
|
||
return;
|
||
}
|
||
|
||
DataService.Instance?.FlushCurrentBFIRecordingSnapshot("打开BFI数据导出界面");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogWarning($"刷新当前BFI记录快照失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void RecoverPendingRecordingJournal()
|
||
{
|
||
try
|
||
{
|
||
var dataService = ServiceLocator.IsRegistered<IDataService>() ? ServiceLocator.Get<IDataService>() as DataService : null;
|
||
if (dataService != null)
|
||
{
|
||
dataService.RecoverPendingBFIRecordingJournal("打开BFI数据导出界面");
|
||
return;
|
||
}
|
||
|
||
DataService.Instance?.RecoverPendingBFIRecordingJournal("打开BFI数据导出界面");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogWarning($"恢复待保存BFI实时日志失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void RefreshHistoryList()
|
||
{
|
||
StopHistoryListBuildCoroutine();
|
||
filteredRecords = SortRecordsByRecentFirst(filteredRecords);
|
||
if (historyItemsByRecordId == null)
|
||
{
|
||
historyItemsByRecordId = new Dictionary<string, BFIHistoryItem>();
|
||
}
|
||
|
||
if (historyScrollRect == null && historyListParent != null)
|
||
{
|
||
historyScrollRect = historyListParent.GetComponentInParent<ScrollRect>();
|
||
}
|
||
|
||
// 清空现有项目
|
||
foreach (var item in historyItems)
|
||
{
|
||
if (item != null && item.gameObject != null)
|
||
Destroy(item.gameObject);
|
||
}
|
||
historyItems.Clear();
|
||
historyItemsByRecordId.Clear();
|
||
|
||
MoveHistoryScrollToNewest();
|
||
UpdateSelectionCount();
|
||
historyListBuildCoroutine = StartCoroutine(BuildHistoryListGradually(filteredRecords));
|
||
}
|
||
|
||
private IEnumerator BuildHistoryListGradually(List<BFITestRecord> records)
|
||
{
|
||
var recordsToBuild = records != null ? new List<BFITestRecord>(records) : new List<BFITestRecord>();
|
||
for (int i = 0; i < recordsToBuild.Count; i++)
|
||
{
|
||
CreateHistoryItem(recordsToBuild[i]);
|
||
|
||
if ((i + 1) % HISTORY_ITEMS_PER_FRAME == 0)
|
||
{
|
||
UpdateSelectionCount();
|
||
yield return null;
|
||
}
|
||
}
|
||
|
||
historyListBuildCoroutine = null;
|
||
UpdateSelectionCount();
|
||
UpdateSelectAllToggle();
|
||
MoveHistoryScrollToNewest();
|
||
}
|
||
|
||
private void StopHistoryListBuildCoroutine()
|
||
{
|
||
if (historyListBuildCoroutine != null)
|
||
{
|
||
StopCoroutine(historyListBuildCoroutine);
|
||
historyListBuildCoroutine = null;
|
||
}
|
||
}
|
||
|
||
private void MoveHistoryScrollToNewest()
|
||
{
|
||
if (historyScrollRect == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Canvas.ForceUpdateCanvases();
|
||
historyScrollRect.verticalNormalizedPosition = 1f;
|
||
}
|
||
|
||
private void CreateHistoryItem(BFITestRecord record)
|
||
{
|
||
if (historyItemPrefab == null || historyListParent == null)
|
||
return;
|
||
|
||
GameObject itemObj = Instantiate(historyItemPrefab, historyListParent);
|
||
BFIHistoryItem historyItem = itemObj.GetComponent<BFIHistoryItem>();
|
||
|
||
if (historyItem == null)
|
||
historyItem = itemObj.AddComponent<BFIHistoryItem>();
|
||
|
||
historyItem.Initialize(record);
|
||
historyItem.OnSelectionChanged += OnItemSelectionChanged;
|
||
historyItem.SetSelected(selectedRecordIds.Contains(record.TestId));
|
||
|
||
historyItems.Add(historyItem);
|
||
if (historyItemsByRecordId == null)
|
||
{
|
||
historyItemsByRecordId = new Dictionary<string, BFIHistoryItem>();
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(record.TestId))
|
||
{
|
||
historyItemsByRecordId[record.TestId] = historyItem;
|
||
}
|
||
}
|
||
|
||
private bool TryUpdateVisibleHistoryItem(BFITestRecord record)
|
||
{
|
||
if (record == null || string.IsNullOrEmpty(record.TestId))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
int filteredIndex = filteredRecords.FindIndex(r => r != null && r.TestId == record.TestId);
|
||
if (filteredIndex < 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
filteredRecords[filteredIndex] = record;
|
||
if (historyItemsByRecordId == null)
|
||
{
|
||
historyItemsByRecordId = new Dictionary<string, BFIHistoryItem>();
|
||
}
|
||
|
||
if (!historyItemsByRecordId.TryGetValue(record.TestId, out var historyItem) || historyItem == null)
|
||
{
|
||
historyItem = historyItems.FirstOrDefault(item => item != null && item.GetRecord()?.TestId == record.TestId);
|
||
if (historyItem == null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
historyItemsByRecordId[record.TestId] = historyItem;
|
||
}
|
||
|
||
historyItem.Initialize(record);
|
||
historyItem.SetSelected(selectedRecordIds.Contains(record.TestId));
|
||
UpdateSelectionCount();
|
||
UpdateSelectAllToggle();
|
||
return true;
|
||
}
|
||
|
||
private void OnItemSelectionChanged(string recordId, bool isSelected)
|
||
{
|
||
if (isSelected)
|
||
{
|
||
selectedRecordIds.Add(recordId);
|
||
}
|
||
else
|
||
{
|
||
selectedRecordIds.Remove(recordId);
|
||
}
|
||
|
||
UpdateSelectionCount();
|
||
UpdateSelectAllToggle();
|
||
}
|
||
|
||
private void UpdateSelectionCount()
|
||
{
|
||
if (selectionCountText != null)
|
||
{
|
||
int totalCount = filteredRecords?.Count ?? 0;
|
||
int visibleCount = historyItems?.Count ?? 0;
|
||
selectionCountText.text = visibleCount < totalCount
|
||
? $"已选择: {selectedRecordIds.Count} / {totalCount}(已显示 {visibleCount})"
|
||
: $"已选择: {selectedRecordIds.Count} / {totalCount}";
|
||
}
|
||
|
||
// 更新导出按钮状态
|
||
if (exportSelectedButton != null)
|
||
exportSelectedButton.interactable = selectedRecordIds.Count > 0;
|
||
}
|
||
|
||
private void UpdateSelectAllToggle()
|
||
{
|
||
if (selectAllToggle != null)
|
||
{
|
||
int filteredCount = filteredRecords?.Count ?? 0;
|
||
bool allSelected = filteredCount > 0 &&
|
||
filteredRecords.All(record => record != null && selectedRecordIds.Contains(record.TestId));
|
||
selectAllToggle.SetIsOnWithoutNotify(allSelected);
|
||
}
|
||
}
|
||
|
||
private void UpdateStatistics()
|
||
{
|
||
try
|
||
{
|
||
if (BFIHistoryManager.Instance != null)
|
||
{
|
||
var stats = BFIHistoryManager.Instance.GetHistoryStatistics();
|
||
|
||
if (totalRecordsText != null)
|
||
totalRecordsText.text = $"总记录数: {stats.TotalRecords}";
|
||
if (totalTestTimeText != null)
|
||
totalTestTimeText.text = $"总测试时间: {stats.TotalTestTime:F1} 分钟";
|
||
if (todayRecordsText != null)
|
||
todayRecordsText.text = $"今日: {stats.TodayRecords}";
|
||
if (weekRecordsText != null)
|
||
weekRecordsText.text = $"本周: {stats.WeekRecords}";
|
||
if (monthRecordsText != null)
|
||
monthRecordsText.text = $"本月: {stats.MonthRecords}";
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"更新统计信息失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#region 事件处理
|
||
|
||
private void OnSearchClicked()
|
||
{
|
||
ApplySearch();
|
||
}
|
||
|
||
private void OnClearSearchClicked()
|
||
{
|
||
if (searchInputField != null)
|
||
searchInputField.text = "";
|
||
ApplySearch();
|
||
}
|
||
|
||
// 清空所有测试数据
|
||
private void OnClearClicked()
|
||
{
|
||
// 显示确认对话框
|
||
ConfirmDialog.ShowDeleteConfirm(
|
||
"所有BFI历史记录",
|
||
() => ClearBFIRecordData(),
|
||
() => Debug.Log("取消清空记录")
|
||
);
|
||
}
|
||
|
||
|
||
public void ClearBFIRecordData()
|
||
{
|
||
try
|
||
{
|
||
if (BFIHistoryManager.Instance != null)
|
||
{
|
||
ResetCurrentRecordingAfterClear();
|
||
BFIHistoryManager.Instance.ClearHistory();
|
||
Debug.Log("BFIHistoryTestHelper: 测试数据已清空");
|
||
LoadHistoryRecords();
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("BFIHistoryTestHelper: BFIHistoryManager.Instance 为空");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"BFIHistoryTestHelper: 清空测试数据失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void ResetCurrentRecordingAfterClear()
|
||
{
|
||
try
|
||
{
|
||
var dataService = ServiceLocator.IsRegistered<IDataService>() ? ServiceLocator.Get<IDataService>() as DataService : null;
|
||
if (dataService != null)
|
||
{
|
||
dataService.ResetCurrentBFIRecordingAfterHistoryClear("清空BFI历史记录");
|
||
return;
|
||
}
|
||
|
||
DataService.Instance?.ResetCurrentBFIRecordingAfterHistoryClear("清空BFI历史记录");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogWarning($"清空后重置当前BFI记录失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void OnSearchInputChanged(string searchText)
|
||
{
|
||
ApplySearch();
|
||
}
|
||
|
||
private void ApplySearch()
|
||
{
|
||
try
|
||
{
|
||
string searchText = searchInputField?.text ?? "";
|
||
|
||
if (string.IsNullOrEmpty(searchText))
|
||
{
|
||
filteredRecords = SortRecordsByRecentFirst(allRecords);
|
||
}
|
||
else if (BFIHistoryManager.Instance != null)
|
||
{
|
||
filteredRecords = SortRecordsByRecentFirst(BFIHistoryManager.Instance.SearchRecords(searchText));
|
||
}
|
||
|
||
RefreshHistoryList();
|
||
UpdateStatus($"搜索结果: {filteredRecords.Count} 条记录");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"搜索失败: {ex.Message}");
|
||
UpdateStatus($"搜索失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void OnApplyDateFilter()
|
||
{
|
||
try
|
||
{
|
||
int filterIndex = dateFilterDropdown?.value ?? 0;
|
||
DateTime startDate, endDate;
|
||
|
||
switch (filterIndex)
|
||
{
|
||
case 0: // 全部时间
|
||
filteredRecords = SortRecordsByRecentFirst(allRecords);
|
||
break;
|
||
|
||
case 1: // 今天
|
||
startDate = DateTime.Today;
|
||
endDate = DateTime.Today.AddDays(1);
|
||
ApplyDateRangeFilter(startDate, endDate);
|
||
break;
|
||
|
||
case 2: // 本周
|
||
startDate = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek);
|
||
endDate = startDate.AddDays(7);
|
||
ApplyDateRangeFilter(startDate, endDate);
|
||
break;
|
||
|
||
case 3: // 本月
|
||
startDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
|
||
endDate = startDate.AddMonths(1);
|
||
ApplyDateRangeFilter(startDate, endDate);
|
||
break;
|
||
|
||
case 4: // 自定义日期范围
|
||
if (DateTime.TryParse(startDateInput?.text, out startDate) &&
|
||
DateTime.TryParse(endDateInput?.text, out endDate))
|
||
{
|
||
ApplyDateRangeFilter(startDate, endDate.AddDays(1));
|
||
}
|
||
else
|
||
{
|
||
UpdateStatus("日期格式错误");
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
|
||
RefreshHistoryList();
|
||
UpdateStatus($"过滤结果: {filteredRecords.Count} 条记录");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"日期过滤失败: {ex.Message}");
|
||
UpdateStatus($"过滤失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void ApplyDateRangeFilter(DateTime startDate, DateTime endDate)
|
||
{
|
||
if (BFIHistoryManager.Instance != null)
|
||
{
|
||
filteredRecords = SortRecordsByRecentFirst(BFIHistoryManager.Instance.GetRecordsByDateRange(startDate, endDate));
|
||
}
|
||
}
|
||
|
||
private void OnSelectAllToggled(bool isOn)
|
||
{
|
||
selectedRecordIds.Clear();
|
||
|
||
if (isOn)
|
||
{
|
||
foreach (var record in filteredRecords)
|
||
{
|
||
if (record != null && !string.IsNullOrEmpty(record.TestId))
|
||
{
|
||
selectedRecordIds.Add(record.TestId);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新所有项目的选择状态
|
||
foreach (var item in historyItems)
|
||
{
|
||
if (item != null)
|
||
{
|
||
item.SetSelected(isOn);
|
||
}
|
||
}
|
||
|
||
UpdateSelectionCount();
|
||
}
|
||
|
||
private void OnSelectTodayClicked()
|
||
{
|
||
selectedRecordIds.Clear();
|
||
|
||
var today = DateTime.Today;
|
||
foreach (var record in filteredRecords)
|
||
{
|
||
if (record.StartTime.Date == today)
|
||
{
|
||
selectedRecordIds.Add(record.TestId);
|
||
}
|
||
}
|
||
|
||
UpdateItemSelections();
|
||
UpdateSelectionCount();
|
||
}
|
||
|
||
private void OnSelectWeekClicked()
|
||
{
|
||
selectedRecordIds.Clear();
|
||
|
||
var weekStart = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek);
|
||
foreach (var record in filteredRecords)
|
||
{
|
||
if (record.StartTime >= weekStart)
|
||
{
|
||
selectedRecordIds.Add(record.TestId);
|
||
}
|
||
}
|
||
|
||
UpdateItemSelections();
|
||
UpdateSelectionCount();
|
||
}
|
||
|
||
private void OnClearSelectionClicked()
|
||
{
|
||
selectedRecordIds.Clear();
|
||
|
||
foreach (var item in historyItems)
|
||
{
|
||
item.SetSelected(false);
|
||
}
|
||
|
||
UpdateSelectionCount();
|
||
if (selectAllToggle != null)
|
||
selectAllToggle.SetIsOnWithoutNotify(false);
|
||
}
|
||
|
||
private void UpdateItemSelections()
|
||
{
|
||
foreach (var item in historyItems)
|
||
{
|
||
if (item == null || item.GetRecord() == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
bool isSelected = selectedRecordIds.Contains(item.GetRecord().TestId);
|
||
item.SetSelected(isSelected);
|
||
}
|
||
|
||
UpdateSelectAllToggle();
|
||
}
|
||
|
||
private void OnExportSelectedClicked()
|
||
{
|
||
if (selectedRecordIds.Count == 0)
|
||
{
|
||
UpdateStatus("请先选择要导出的记录");
|
||
return;
|
||
}
|
||
|
||
ExportRecords(selectedRecordIds.ToList());
|
||
}
|
||
|
||
private void OnExportAllClicked()
|
||
{
|
||
var allIds = filteredRecords.Select(r => r.TestId).ToList();
|
||
ExportRecords(allIds);
|
||
}
|
||
|
||
private void OnPreviewClicked()
|
||
{
|
||
// 显示导出预览
|
||
if (selectedRecordIds.Count > 0)
|
||
{
|
||
ShowExportPreview(selectedRecordIds.ToList());
|
||
}
|
||
else
|
||
{
|
||
UpdateStatus("请先选择要预览的记录");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 导出功能
|
||
|
||
private async void ExportRecords(List<string> recordIds)
|
||
{
|
||
try
|
||
{
|
||
UpdateStatus("正在准备导出...");
|
||
lastExportedFilePath = null;
|
||
lastExportFailureMessage = null;
|
||
|
||
var selectedRecords = new List<BFITestRecord>();
|
||
foreach (var id in recordIds)
|
||
{
|
||
var record = BFIHistoryManager.Instance != null
|
||
? BFIHistoryManager.Instance.GetFullRecord(id)
|
||
: allRecords.FirstOrDefault(r => r.TestId == id);
|
||
if (record != null)
|
||
{
|
||
selectedRecords.Add(record);
|
||
}
|
||
}
|
||
|
||
if (selectedRecords.Count == 0)
|
||
{
|
||
UpdateStatus("没有找到可导出的记录");
|
||
return;
|
||
}
|
||
|
||
// 获取导出格式
|
||
ExportFormat format = GetSelectedExportFormat();
|
||
|
||
UpdateStatus($"正在导出 {selectedRecords.Count} 条记录...");
|
||
|
||
// 执行导出
|
||
bool success = await ExportRecordsAsync(selectedRecords, format);
|
||
|
||
if (success)
|
||
{
|
||
UpdateStatus($"导出成功!已导出 {selectedRecords.Count} 条记录");
|
||
ShowExportSuccessDialog(selectedRecords.Count, format, lastExportedFilePath);
|
||
}
|
||
else
|
||
{
|
||
string failureMessage = string.IsNullOrEmpty(lastExportFailureMessage)
|
||
? "导出失败,请检查U盘、存储空间和权限"
|
||
: lastExportFailureMessage;
|
||
UpdateStatus(failureMessage);
|
||
ShowExportFailureDialog(failureMessage);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"导出失败: {ex.Message}");
|
||
UpdateStatus("导出失败");
|
||
ShowExportFailureDialog($"导出失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private ExportFormat GetSelectedExportFormat()
|
||
{
|
||
int formatIndex = exportFormatDropdown?.value ?? 0;
|
||
return (ExportFormat)formatIndex;
|
||
}
|
||
|
||
private async System.Threading.Tasks.Task<bool> ExportRecordsAsync(List<BFITestRecord> records, ExportFormat format)
|
||
{
|
||
await System.Threading.Tasks.Task.Yield();
|
||
|
||
try
|
||
{
|
||
records = EnsureFullRecordsForExport(records);
|
||
if (records == null || records.Count == 0)
|
||
{
|
||
if (string.IsNullOrEmpty(lastExportFailureMessage))
|
||
{
|
||
lastExportFailureMessage = "没有找到可导出的完整BFI记录";
|
||
}
|
||
return false;
|
||
}
|
||
|
||
var exportService = new DataExportService();
|
||
int totalPointCount = records.Sum(record => record?.DataPoints?.Count ?? 0);
|
||
Debug.Log($"BFI导出完整记录准备完成: 记录数={records.Count}, 数据点={totalPointCount}, 格式={format}");
|
||
Debug.Log("BFI导出开始检查U盘目录授权");
|
||
|
||
// 先做权限和目录可用性检查,避免仅返回通用失败文案
|
||
if (!exportService.CheckExportPermissions())
|
||
{
|
||
string errorMessage = exportService.GetLastErrorMessage();
|
||
Debug.LogWarning($"导出权限检查未通过,原因: {errorMessage}");
|
||
if (!string.IsNullOrEmpty(errorMessage))
|
||
{
|
||
lastExportFailureMessage = errorMessage;
|
||
UpdateStatus(errorMessage);
|
||
}
|
||
lastExportedFilePath = null;
|
||
return false;
|
||
}
|
||
|
||
bool success = exportService.ExportHistoryRecords(records, format);
|
||
lastExportedFilePath = success ? exportService.GetLastExportedFilePath() : null;
|
||
|
||
if (!success)
|
||
{
|
||
string errorMessage = exportService.GetLastErrorMessage();
|
||
Debug.LogWarning($"导出服务返回失败,格式: {format},原因: {errorMessage}");
|
||
if (!string.IsNullOrEmpty(errorMessage))
|
||
{
|
||
lastExportFailureMessage = errorMessage;
|
||
UpdateStatus(errorMessage);
|
||
}
|
||
}
|
||
|
||
// 记录导出操作日志
|
||
if (success)
|
||
{
|
||
var authService = ServiceLocator.Get<IAuthenticationService>();
|
||
var logService = ServiceLocator.Get<UserLogService>();
|
||
|
||
// 统计导出信息
|
||
string patientNames = string.Join(", ", records.Select(r => r.PatientName).Distinct().Take(3));
|
||
if (records.Select(r => r.PatientName).Distinct().Count() > 3)
|
||
patientNames += "等";
|
||
|
||
logService?.LogUserOperation(
|
||
authService?.CurrentUsername ?? "system",
|
||
"导出BFI历史记录",
|
||
$"导出{records.Count}条记录, 格式: {format}, 患者: {patientNames}",
|
||
true);
|
||
}
|
||
|
||
return success;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"异步导出失败: {ex.Message}");
|
||
lastExportFailureMessage = $"导出失败: {ex.Message}";
|
||
|
||
// 记录导出失败日志
|
||
var authService = ServiceLocator.Get<IAuthenticationService>();
|
||
var logService = ServiceLocator.Get<UserLogService>();
|
||
logService?.LogUserOperation(
|
||
authService?.CurrentUsername ?? "system",
|
||
"导出BFI历史记录",
|
||
$"失败: {ex.Message}",
|
||
false);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private List<BFITestRecord> EnsureFullRecordsForExport(List<BFITestRecord> records)
|
||
{
|
||
var fullRecords = new List<BFITestRecord>();
|
||
var missingDetailRecords = new List<string>();
|
||
if (records == null)
|
||
{
|
||
return fullRecords;
|
||
}
|
||
|
||
foreach (var record in records)
|
||
{
|
||
if (record == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
BFITestRecord fullRecord = record;
|
||
bool needsFullRecord = record.DataPoints == null || record.DataPoints.Count == 0;
|
||
if (needsFullRecord && BFIHistoryManager.Instance != null)
|
||
{
|
||
fullRecord = BFIHistoryManager.Instance.GetFullRecord(record.TestId) ?? record;
|
||
}
|
||
|
||
if (fullRecord.DataPoints == null || fullRecord.DataPoints.Count == 0)
|
||
{
|
||
string displayName = string.IsNullOrEmpty(fullRecord.TestName) ? fullRecord.TestId : fullRecord.TestName;
|
||
missingDetailRecords.Add(displayName);
|
||
Debug.LogWarning($"BFI导出记录缺少明细数据: {fullRecord.TestName}, ID: {fullRecord.TestId}, 文件: {fullRecord.FilePath}");
|
||
continue;
|
||
}
|
||
|
||
fullRecords.Add(fullRecord);
|
||
}
|
||
|
||
if (missingDetailRecords.Count > 0)
|
||
{
|
||
string names = string.Join("、", missingDetailRecords.Take(3));
|
||
if (missingDetailRecords.Count > 3)
|
||
{
|
||
names += "等";
|
||
}
|
||
|
||
lastExportFailureMessage = $"所选记录中有 {missingDetailRecords.Count} 条缺少BFI明细数据,无法导出:{names}";
|
||
fullRecords.Clear();
|
||
}
|
||
|
||
return fullRecords;
|
||
}
|
||
|
||
private void ShowExportPreview(List<string> recordIds)
|
||
{
|
||
// 实现导出预览功能
|
||
// 这里可以显示一个预览窗口,显示将要导出的数据
|
||
UpdateStatus($"预览 {recordIds.Count} 条记录的导出内容");
|
||
}
|
||
|
||
private void ShowExportSuccessDialog(int recordCount, ExportFormat format, string filePath)
|
||
{
|
||
string message = $"已成功导出 {recordCount} 条BFI历史记录\n导出格式: {GetExportFormatDisplayName(format)}";
|
||
if (!string.IsNullOrEmpty(filePath))
|
||
{
|
||
message += $"\n文件路径: {filePath}";
|
||
}
|
||
|
||
ConfirmDialog.Show("导出成功", message, () => { }, null, "确认", "取消", false, false);
|
||
}
|
||
|
||
private void ShowExportFailureDialog(string message)
|
||
{
|
||
ConfirmDialog.Show("导出失败", message, () => { }, null, "确认", "取消", true, false);
|
||
}
|
||
|
||
private string GetExportFormatDisplayName(ExportFormat format)
|
||
{
|
||
switch (format)
|
||
{
|
||
case ExportFormat.CSV:
|
||
return "CSV数据";
|
||
case ExportFormat.PDF:
|
||
return "PDF报告";
|
||
case ExportFormat.TXT:
|
||
default:
|
||
return "文本报告";
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 历史管理器事件
|
||
|
||
private void OnRecordAdded(BFITestRecord record)
|
||
{
|
||
allRecords.Add(record);
|
||
ApplyCurrentFilter();
|
||
UpdateStatistics();
|
||
}
|
||
|
||
private void OnRecordUpdated(BFITestRecord record)
|
||
{
|
||
var existingRecord = allRecords.FirstOrDefault(r => r.TestId == record.TestId);
|
||
if (existingRecord != null)
|
||
{
|
||
int index = allRecords.IndexOf(existingRecord);
|
||
allRecords[index] = record;
|
||
if (!TryUpdateVisibleHistoryItem(record))
|
||
{
|
||
// 记录当前不可见时不重建列表;搜索/筛选操作会重新计算可见集合。
|
||
UpdateSelectionCount();
|
||
UpdateSelectAllToggle();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OnRecordRemoved(string recordId)
|
||
{
|
||
allRecords.RemoveAll(r => r.TestId == recordId);
|
||
selectedRecordIds.Remove(recordId);
|
||
ApplyCurrentFilter();
|
||
UpdateStatistics();
|
||
}
|
||
|
||
private void OnHistoryLoaded()
|
||
{
|
||
LoadHistoryRecords();
|
||
}
|
||
|
||
private void ApplyCurrentFilter()
|
||
{
|
||
// 重新应用当前的搜索和过滤条件
|
||
ApplySearch();
|
||
OnApplyDateFilter();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region UI辅助方法
|
||
|
||
private void UpdateStatus(string message)
|
||
{
|
||
if (statusText != null)
|
||
{
|
||
statusText.text = message;
|
||
Debug.Log($"状态更新: {message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|