265 lines
6.9 KiB
C#
265 lines
6.9 KiB
C#
|
|
using System;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.UI;
|
|||
|
|
using TMPro;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// BFI历史记录列表项UI组件
|
|||
|
|
/// 用于在BFIHistoryExportPanel中显示单个历史记录
|
|||
|
|
/// </summary>
|
|||
|
|
public class BFIHistoryItem : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
[Header("UI组件")]
|
|||
|
|
[SerializeField] private Toggle selectionToggle;
|
|||
|
|
[SerializeField] private TextMeshProUGUI testNameText;
|
|||
|
|
[SerializeField] private TextMeshProUGUI patientNameText;
|
|||
|
|
[SerializeField] private TextMeshProUGUI testTimeText;
|
|||
|
|
[SerializeField] private TextMeshProUGUI durationText;
|
|||
|
|
[SerializeField] private TextMeshProUGUI bfiRangeText;
|
|||
|
|
[SerializeField] private Image statusImage;
|
|||
|
|
|
|||
|
|
[Header("状态颜色")]
|
|||
|
|
[SerializeField] private Color normalColor = Color.green;
|
|||
|
|
[SerializeField] private Color warningColor = Color.yellow;
|
|||
|
|
[SerializeField] private Color errorColor = Color.red;
|
|||
|
|
|
|||
|
|
// 事件
|
|||
|
|
public event System.Action<string, bool> OnSelectionChanged;
|
|||
|
|
|
|||
|
|
private BFITestRecord _record;
|
|||
|
|
|
|||
|
|
private void Awake()
|
|||
|
|
{
|
|||
|
|
InitializeUI();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void InitializeUI()
|
|||
|
|
{
|
|||
|
|
// 选择框事件
|
|||
|
|
if (selectionToggle != null)
|
|||
|
|
{
|
|||
|
|
selectionToggle.onValueChanged.AddListener(OnToggleChanged);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 初始化历史记录项
|
|||
|
|
/// </summary>
|
|||
|
|
public void Initialize(BFITestRecord record)
|
|||
|
|
{
|
|||
|
|
_record = record;
|
|||
|
|
UpdateDisplay();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新显示内容
|
|||
|
|
/// </summary>
|
|||
|
|
private void UpdateDisplay()
|
|||
|
|
{
|
|||
|
|
if (_record == null) return;
|
|||
|
|
|
|||
|
|
// 测试名称
|
|||
|
|
if (testNameText != null)
|
|||
|
|
{
|
|||
|
|
testNameText.text = _record.TestName;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 患者姓名
|
|||
|
|
if (patientNameText != null)
|
|||
|
|
{
|
|||
|
|
string patientInfo = !string.IsNullOrEmpty(_record.PatientName) ?
|
|||
|
|
_record.PatientName : "未知患者";
|
|||
|
|
if (!string.IsNullOrEmpty(_record.PatientId))
|
|||
|
|
{
|
|||
|
|
patientInfo += $" ({_record.PatientId})";
|
|||
|
|
}
|
|||
|
|
patientNameText.text = patientInfo;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试时间
|
|||
|
|
if (testTimeText != null)
|
|||
|
|
{
|
|||
|
|
testTimeText.text = _record.StartTime.ToString("MM-dd HH:mm");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 持续时间
|
|||
|
|
if (durationText != null)
|
|||
|
|
{
|
|||
|
|
var duration = _record.GetDurationMinutes();
|
|||
|
|
durationText.text = $"{duration:F1}分钟";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// BFI范围
|
|||
|
|
if (bfiRangeText != null && _record.Statistics != null)
|
|||
|
|
{
|
|||
|
|
bfiRangeText.text = GetBfiRangeDisplayText();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 状态指示器
|
|||
|
|
UpdateStatusIndicator();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新状态指示器
|
|||
|
|
/// </summary>
|
|||
|
|
private void UpdateStatusIndicator()
|
|||
|
|
{
|
|||
|
|
if (statusImage == null || _record?.Statistics == null) return;
|
|||
|
|
|
|||
|
|
// 根据BFI统计信息确定状态
|
|||
|
|
var stats = _record.Statistics;
|
|||
|
|
Color statusColor = normalColor;
|
|||
|
|
|
|||
|
|
if (stats.Count <= 0)
|
|||
|
|
{
|
|||
|
|
statusImage.color = statusColor;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否有异常值(简单规则:超出30-70范围)
|
|||
|
|
if (stats.MinValue < 30f || stats.MaxValue > 70f)
|
|||
|
|
{
|
|||
|
|
statusColor = warningColor;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否有严重异常(超出20-80范围)
|
|||
|
|
if (stats.MinValue < 20f || stats.MaxValue > 80f)
|
|||
|
|
{
|
|||
|
|
statusColor = errorColor;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
statusImage.color = statusColor;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string GetBfiRangeDisplayText()
|
|||
|
|
{
|
|||
|
|
var stats = _record?.Statistics;
|
|||
|
|
if (stats == null || stats.Count <= 0)
|
|||
|
|
{
|
|||
|
|
return "无数据";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (IsInvalidRange(stats.MinValue, stats.MaxValue) && TryGetRangeFromDataPoints(out float minValue, out float maxValue))
|
|||
|
|
{
|
|||
|
|
return $"{minValue:F1}-{maxValue:F1}";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return $"{stats.MinValue:F1}-{stats.MaxValue:F1}";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool IsInvalidRange(float minValue, float maxValue)
|
|||
|
|
{
|
|||
|
|
return float.IsNaN(minValue) ||
|
|||
|
|
float.IsInfinity(minValue) ||
|
|||
|
|
float.IsNaN(maxValue) ||
|
|||
|
|
float.IsInfinity(maxValue) ||
|
|||
|
|
maxValue < minValue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool TryGetRangeFromDataPoints(out float minValue, out float maxValue)
|
|||
|
|
{
|
|||
|
|
minValue = float.MaxValue;
|
|||
|
|
maxValue = float.MinValue;
|
|||
|
|
|
|||
|
|
if (_record?.DataPoints == null || _record.DataPoints.Count == 0)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool hasValidPoint = false;
|
|||
|
|
foreach (var point in _record.DataPoints)
|
|||
|
|
{
|
|||
|
|
if (point == null || float.IsNaN(point.BFI) || float.IsInfinity(point.BFI))
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
minValue = Mathf.Min(minValue, point.BFI);
|
|||
|
|
maxValue = Mathf.Max(maxValue, point.BFI);
|
|||
|
|
hasValidPoint = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return hasValidPoint;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 设置选择状态
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetSelected(bool selected)
|
|||
|
|
{
|
|||
|
|
if (selectionToggle != null)
|
|||
|
|
{
|
|||
|
|
selectionToggle.SetIsOnWithoutNotify(selected);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取当前选择状态
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsSelected()
|
|||
|
|
{
|
|||
|
|
return selectionToggle != null ? selectionToggle.isOn : false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取关联的记录
|
|||
|
|
/// </summary>
|
|||
|
|
public BFITestRecord GetRecord()
|
|||
|
|
{
|
|||
|
|
return _record;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region 事件处理
|
|||
|
|
|
|||
|
|
private void OnToggleChanged(bool isOn)
|
|||
|
|
{
|
|||
|
|
if (_record != null)
|
|||
|
|
{
|
|||
|
|
OnSelectionChanged?.Invoke(_record.TestId, isOn);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 高亮显示(用于搜索结果)
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetHighlight(bool highlight)
|
|||
|
|
{
|
|||
|
|
var bg = GetComponent<Image>();
|
|||
|
|
if (bg != null)
|
|||
|
|
{
|
|||
|
|
bg.color = highlight ? new Color(1f, 1f, 0f, 0.3f) : Color.white;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新时间显示格式
|
|||
|
|
/// </summary>
|
|||
|
|
public void SetTimeDisplayFormat(bool showFullDate)
|
|||
|
|
{
|
|||
|
|
if (testTimeText != null && _record != null)
|
|||
|
|
{
|
|||
|
|
if (showFullDate)
|
|||
|
|
{
|
|||
|
|
testTimeText.text = _record.StartTime.ToString("yyyy-MM-dd HH:mm");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 如果是今天,只显示时间;如果是其他天,显示月-日
|
|||
|
|
var today = DateTime.Today;
|
|||
|
|
if (_record.StartTime.Date == today)
|
|||
|
|
{
|
|||
|
|
testTimeText.text = _record.StartTime.ToString("HH:mm");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
testTimeText.text = _record.StartTime.ToString("MM-dd HH:mm");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|