feat: 告警信息UI优化与中文化 v1.3.6

- 修复健康数据弹窗白色文字不可见问题
- 优化健康数据弹窗样式,采用现代化设计
- 告警类型、状态、严重级别中英文映射
- 添加彩色标签系统,提升视觉识别度
- 增强用户体验与界面美观度
This commit is contained in:
2025-08-26 21:52:19 +08:00
parent 6fa066bd1b
commit 038d2df078
+134 -30
View File
@@ -27,6 +27,99 @@ const { hasAuth } = useAuth();
const { dictTag } = useDict();
// 告警类型中英文映射
const alertTypeMap = {
'Heartrate Low': '心率过低',
'Heartrate High': '心率过高',
'Blood Pressure Low': '血压过低',
'Blood Pressure High': '血压过高',
'Temperature Low': '体温过低',
'Temperature High': '体温过高',
'Blood Oxygen Low': '血氧过低',
'Blood Oxygen High': '血氧过高',
'Stress High': '压力过高',
'Step Low': '步数不足',
'Location Alert': '位置告警',
'Device Offline': '设备离线',
'Data Abnormal': '数据异常'
};
// 告警状态中英文映射
const alertStatusMap = {
'pending': '待处理',
'processing': '处理中',
'responded': '已处理',
'resolved': '已解决',
'closed': '已关闭'
};
// 严重级别中英文映射
const severityLevelMap = {
'low': '低',
'medium': '中',
'high': '高',
'critical': '紧急'
};
// 获取告警类型标签颜色
const getAlertTypeColor = (type: string) => {
const colorMap: Record<string, string> = {
'Heartrate Low': '#ff4d4f',
'Heartrate High': '#ff7875',
'Blood Pressure Low': '#faad14',
'Blood Pressure High': '#fa8c16',
'Temperature Low': '#1890ff',
'Temperature High': '#ff4d4f',
'Blood Oxygen Low': '#722ed1',
'Blood Oxygen High': '#9254de',
'Stress High': '#f5222d',
'Step Low': '#52c41a',
'Location Alert': '#13c2c2',
'Device Offline': '#666',
'Data Abnormal': '#fa541c'
};
return colorMap[type] || '#666';
};
// 增强的字典标签函数,支持告警类型、状态、严重级别中文映射
const enhancedDictTag = (code: string, value: string | null) => {
if (!value) return null;
if (code === 'alert_type' && alertTypeMap[value as keyof typeof alertTypeMap]) {
const chineseValue = alertTypeMap[value as keyof typeof alertTypeMap];
const color = getAlertTypeColor(value);
return <span style={`padding: 4px 8px; background-color: ${color}15; border: 1px solid ${color}40; border-radius: 6px; font-size: 12px; color: ${color}; font-weight: 500;`}>{chineseValue}</span>;
}
if (code === 'alert_status' && alertStatusMap[value as keyof typeof alertStatusMap]) {
const chineseValue = alertStatusMap[value as keyof typeof alertStatusMap];
const statusColors: Record<string, string> = {
'pending': '#faad14',
'processing': '#1890ff',
'responded': '#52c41a',
'resolved': '#52c41a',
'closed': '#666'
};
const color = statusColors[value] || '#666';
return <span style={`padding: 4px 8px; background-color: ${color}15; border: 1px solid ${color}40; border-radius: 6px; font-size: 12px; color: ${color}; font-weight: 500;`}>{chineseValue}</span>;
}
if (code === 'severity_level' && severityLevelMap[value as keyof typeof severityLevelMap]) {
const chineseValue = severityLevelMap[value as keyof typeof severityLevelMap];
const levelColors: Record<string, string> = {
'low': '#52c41a',
'medium': '#faad14',
'high': '#fa8c16',
'critical': '#ff4d4f'
};
const color = levelColors[value] || '#666';
return <span style={`padding: 4px 8px; background-color: ${color}15; border: 1px solid ${color}40; border-radius: 6px; font-size: 12px; color: ${color}; font-weight: 500;`}>{chineseValue}</span>;
}
// 否则使用原来的字典标签
return dictTag(code, value);
};
const editingData: Ref<Api.Health.AlertInfo | null> = ref(null);
const customerId = authStore.userInfo?.customerId;
@@ -67,7 +160,7 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
title: $t('page.health.alert.info.alertType'),
align: 'center',
minWidth: 100,
render: row => dictTag('alert_type', row.alertType)
render: row => enhancedDictTag('alert_type', row.alertType)
},
{
@@ -75,7 +168,7 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
title: $t('page.health.alert.info.alertStatus'),
align: 'center',
minWidth: 100,
render: row => dictTag('alert_status', row.alertStatus)
render: row => enhancedDictTag('alert_status', row.alertStatus)
},
{
key: 'alertDesc' as any as any,
@@ -123,45 +216,56 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
};
const renderHealthInfo = () => {
if (fetchLoading.value) return <div style="color: #1890ff;">⏳ 数据加载中...</div>;
if (fetchError.value) return <div style="color: #ff4d4f;">❌ {fetchError.value}</div>;
if (!healthInfo.value) return <div style="color: #999;">📋 暂无数据</div>;
if (fetchLoading.value) return <div style="color: #1890ff; padding: 12px;">⏳ 数据加载中...</div>;
if (fetchError.value) return <div style="color: #ff4d4f; padding: 12px;">❌ {fetchError.value}</div>;
if (!healthInfo.value) return <div style="color: #666; padding: 12px;">📋 暂无数据</div>;
const d = healthInfo.value; // #简化变量名
const d = healthInfo.value;
return (
<div style="max-width: 350px; padding: 12px; font-size: 13px; line-height: 1.6; background: #fafafa; border-radius: 6px;">
<div style="margin-bottom: 6px; display: flex; justify-content: space-between;">
<span><strong>💓 心率:</strong></span>
<span style="color: #1890ff; font-weight: 500;">{formatValue(d.heartRate, ' bpm')}</span>
<div style="max-width: 380px; padding: 16px; font-size: 14px; line-height: 1.8; background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); border: 1px solid #e2e8f0;">
<div style="margin-bottom: 12px; font-size: 15px; font-weight: 600; color: #1e293b; text-align: center; padding-bottom: 8px; border-bottom: 2px solid #e2e8f0;">
🏥 健康数据详情
</div>
<div style="margin-bottom: 6px; display: flex; justify-content: space-between;">
<span><strong>🩸 血压:</strong></span>
<span style="color: #52c41a; font-weight: 500;">{formatValue(d.pressureHigh)}/{formatValue(d.pressureLow)} mmHg</span>
<div style="margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; background: rgba(255,255,255,0.7); border-radius: 8px; border-left: 4px solid #ef4444;">
<span style="color: #374151; font-weight: 600;"><strong>💓 心率:</strong></span>
<span style="color: #ef4444; font-weight: 700; font-size: 15px;">{formatValue(d.heartRate, ' bpm')}</span>
</div>
<div style="margin-bottom: 6px; display: flex; justify-content: space-between;">
<span><strong>🌡️ 体温:</strong></span>
<span style="color: #fa8c16; font-weight: 500;">{formatValue(d.temperature, '°C')}</span>
<div style="margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; background: rgba(255,255,255,0.7); border-radius: 8px; border-left: 4px solid #22c55e;">
<span style="color: #374151; font-weight: 600;"><strong>🩸 血压:</strong></span>
<span style="color: #22c55e; font-weight: 700; font-size: 15px;">{formatValue(d.pressureHigh)}/{formatValue(d.pressureLow)} mmHg</span>
</div>
<div style="margin-bottom: 6px; display: flex; justify-content: space-between;">
<span><strong>🫁 血氧:</strong></span>
<span style="color: #722ed1; font-weight: 500;">{formatValue(d.bloodOxygen, '%')}</span>
<div style="margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; background: rgba(255,255,255,0.7); border-radius: 8px; border-left: 4px solid #f59e0b;">
<span style="color: #374151; font-weight: 600;"><strong>🌡️ 体温:</strong></span>
<span style="color: #f59e0b; font-weight: 700; font-size: 15px;">{formatValue(d.temperature, '°C')}</span>
</div>
<div style="margin-bottom: 6px; display: flex; justify-content: space-between;">
<span><strong>😰 压力:</strong></span>
<span style="color: #f5222d; font-weight: 500;">{formatValue(d.stress)}</span>
<div style="margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; background: rgba(255,255,255,0.7); border-radius: 8px; border-left: 4px solid #8b5cf6;">
<span style="color: #374151; font-weight: 600;"><strong>🫁 血氧:</strong></span>
<span style="color: #8b5cf6; font-weight: 700; font-size: 15px;">{formatValue(d.bloodOxygen, '%')}</span>
</div>
<div style="margin-bottom: 6px; display: flex; justify-content: space-between;">
<span><strong>👟 步数:</strong></span>
<span style="color: #13c2c2; font-weight: 500;">{formatValue(d.step)}</span>
<div style="margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; background: rgba(255,255,255,0.7); border-radius: 8px; border-left: 4px solid #f97316;">
<span style="color: #374151; font-weight: 600;"><strong>😰 压力:</strong></span>
<span style="color: #f97316; font-weight: 700; font-size: 15px;">{formatValue(d.stress)}</span>
</div>
<div style="margin-bottom: 8px; border-top: 1px solid #e8e8e8; padding-top: 6px;">
<div style="font-size: 12px; color: #666; margin-bottom: 4px;"><strong>📍 位置信息:</strong></div>
<div style="font-size: 11px; color: #999;">
<div style="margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; background: rgba(255,255,255,0.7); border-radius: 8px; border-left: 4px solid #06b6d4;">
<span style="color: #374151; font-weight: 600;"><strong>👟 步数:</strong></span>
<span style="color: #06b6d4; font-weight: 700; font-size: 15px;">{formatValue(d.step)}</span>
</div>
<div style="margin-bottom: 10px; padding: 10px 12px; background: rgba(255,255,255,0.9); border-radius: 8px; border-top: 2px solid #64748b;">
<div style="font-size: 13px; color: #475569; margin-bottom: 6px; font-weight: 600;"><strong>📍 位置信息:</strong></div>
<div style="font-size: 12px; color: #64748b; line-height: 1.4;">
{d.latitude && d.longitude ? `${d.latitude.toFixed(6)}°, ${d.longitude.toFixed(6)}°` : '无位置数据'}
{d.altitude > 0 ? ` (海拔${d.altitude}m)` : ''}
</div>
</div>
<div style="font-size: 11px; color: #999; text-align: center; padding-top: 4px; border-top: 1px solid #e8e8e8;">
<div style="font-size: 12px; color: #64748b; text-align: center; padding: 8px 12px; background: rgba(255,255,255,0.9); border-radius: 8px; font-weight: 500;">
📅 {d.timestamp ? new Date(d.timestamp).toLocaleString('zh-CN') : '时间未知'}
</div>
</div>
@@ -187,7 +291,7 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
title: $t('page.health.alert.info.severityLevel'),
align: 'center',
minWidth: 100,
render: row => dictTag('severity_level', row.severityLevel)
render: row => enhancedDictTag('severity_level', row.severityLevel)
},
{
key: 'alertTimestamp' as any as any,