|
|
@ -1,24 +1,26 @@ |
|
|
<template> |
|
|
<template> |
|
|
<div class="health-dashboard"> |
|
|
<div class="health-dashboard"> |
|
|
<!-- 主内容区域 --> |
|
|
|
|
|
|
|
|
<!-- 主内容区域:Grid三列布局 --> |
|
|
<div class="dashboard-main"> |
|
|
<div class="dashboard-main"> |
|
|
<!-- 左侧:驼圈列表 --> |
|
|
|
|
|
|
|
|
<!-- 左侧:驼圈列表 + 健康统计总览 + 健康预警 --> |
|
|
<div class="col-left"> |
|
|
<div class="col-left"> |
|
|
<div class="card pen-list-card"> |
|
|
<div class="card pen-list-card"> |
|
|
<div class="card-header"> |
|
|
<div class="card-header"> |
|
|
<span class="title">🐫 驼圈列表</span> |
|
|
<span class="title">🐫 驼圈列表</span> |
|
|
<span class="sub">共 {{ pens.length }} 个圈舍</span> |
|
|
<span class="sub">共 {{ pens.length }} 个圈舍</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="pen-grid"> |
|
|
|
|
|
<div |
|
|
|
|
|
v-for="pen in pens" |
|
|
|
|
|
:key="pen.name" |
|
|
|
|
|
class="pen-item" |
|
|
|
|
|
:class="{ active: selectedPen === pen.name }" |
|
|
|
|
|
@click="selectPen(pen.name)" |
|
|
|
|
|
> |
|
|
|
|
|
<div class="pen-name">{{ pen.name }}</div> |
|
|
|
|
|
<div class="pen-count">{{ pen.count }}峰</div> |
|
|
|
|
|
|
|
|
<div class="pen-grid-wrapper"> |
|
|
|
|
|
<div class="pen-grid"> |
|
|
|
|
|
<div |
|
|
|
|
|
v-for="pen in pens" |
|
|
|
|
|
:key="pen.name" |
|
|
|
|
|
class="pen-item" |
|
|
|
|
|
:class="{ active: selectedPen === pen.name }" |
|
|
|
|
|
@click="selectPen(pen.name)" |
|
|
|
|
|
> |
|
|
|
|
|
<div class="pen-name">{{ pen.name }}</div> |
|
|
|
|
|
<div class="pen-count">{{ pen.count }}峰</div> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
@ -66,20 +68,22 @@ |
|
|
<span class="title">⚠️ 健康预警</span> |
|
|
<span class="title">⚠️ 健康预警</span> |
|
|
<span class="badge">{{ healthAlerts.length }}</span> |
|
|
<span class="badge">{{ healthAlerts.length }}</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="alert-list"> |
|
|
|
|
|
<div v-for="(alert, idx) in healthAlerts" :key="idx" class="alert-item"> |
|
|
|
|
|
<span class="alert-level" :class="alert.level">{{ alert.levelText }}</span> |
|
|
|
|
|
<div class="alert-content"> |
|
|
|
|
|
<div class="alert-title">{{ alert.title }}</div> |
|
|
|
|
|
<div class="alert-desc">{{ alert.desc }}</div> |
|
|
|
|
|
|
|
|
<div class="alert-list-wrapper"> |
|
|
|
|
|
<div class="alert-list"> |
|
|
|
|
|
<div v-for="(alert, idx) in healthAlerts" :key="idx" class="alert-item"> |
|
|
|
|
|
<span class="alert-level" :class="alert.level">{{ alert.levelText }}</span> |
|
|
|
|
|
<div class="alert-content"> |
|
|
|
|
|
<div class="alert-title">{{ alert.title }}</div> |
|
|
|
|
|
<div class="alert-desc">{{ alert.desc }}</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
<span class="alert-time">{{ alert.time }}</span> |
|
|
</div> |
|
|
</div> |
|
|
<span class="alert-time">{{ alert.time }}</span> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 中间:驼圈详情 + 健康指标趋势 --> |
|
|
|
|
|
|
|
|
<!-- 中间:圈舍详情 + 健康指标趋势 + 个体骆驼健康数据 --> |
|
|
<div class="col-center"> |
|
|
<div class="col-center"> |
|
|
<div class="card pen-detail-card"> |
|
|
<div class="card pen-detail-card"> |
|
|
<div class="card-header"> |
|
|
<div class="card-header"> |
|
|
@ -142,31 +146,33 @@ |
|
|
class="search-input" |
|
|
class="search-input" |
|
|
/> |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
<div class="camel-grid"> |
|
|
|
|
|
<div |
|
|
|
|
|
v-for="camel in filteredCamels" |
|
|
|
|
|
:key="camel.id" |
|
|
|
|
|
class="camel-item" |
|
|
|
|
|
:class="{ warning: camel.healthStatus !== 'healthy' }" |
|
|
|
|
|
@click="selectCamel(camel)" |
|
|
|
|
|
> |
|
|
|
|
|
<div class="camel-avatar"> |
|
|
|
|
|
<span class="camel-icon">🐪</span> |
|
|
|
|
|
<span v-if="camel.healthStatus !== 'healthy'" class="warning-dot"></span> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="camel-info"> |
|
|
|
|
|
<div class="camel-id">{{ camel.id }}</div> |
|
|
|
|
|
<div class="camel-temp">{{ camel.temperature }}°C</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="camel-status" :class="camel.healthStatus"> |
|
|
|
|
|
{{ getHealthStatusText(camel.healthStatus) }} |
|
|
|
|
|
|
|
|
<div class="camel-grid-wrapper"> |
|
|
|
|
|
<div class="camel-grid"> |
|
|
|
|
|
<div |
|
|
|
|
|
v-for="camel in filteredCamels" |
|
|
|
|
|
:key="camel.id" |
|
|
|
|
|
class="camel-item" |
|
|
|
|
|
:class="{ warning: camel.healthStatus !== 'healthy' }" |
|
|
|
|
|
@click="selectCamel(camel)" |
|
|
|
|
|
> |
|
|
|
|
|
<div class="camel-avatar"> |
|
|
|
|
|
<span class="camel-icon">🐪</span> |
|
|
|
|
|
<span v-if="camel.healthStatus !== 'healthy'" class="warning-dot"></span> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="camel-info"> |
|
|
|
|
|
<div class="camel-id">{{ camel.id }}</div> |
|
|
|
|
|
<div class="camel-temp">{{ camel.temperature }}°C</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="camel-status" :class="camel.healthStatus"> |
|
|
|
|
|
{{ getHealthStatusText(camel.healthStatus) }} |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 右侧:个体骆驼详细健康报告 --> |
|
|
|
|
|
|
|
|
<!-- 右侧:个体骆驼详细健康报告 + 健康建议 --> |
|
|
<div class="col-right"> |
|
|
<div class="col-right"> |
|
|
<div class="card health-report-card"> |
|
|
<div class="card health-report-card"> |
|
|
<div class="card-header"> |
|
|
<div class="card-header"> |
|
|
@ -225,7 +231,7 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="report-timeline"> |
|
|
|
|
|
|
|
|
<div class="report-timeline compact-timeline"> |
|
|
<div class="timeline-title">📅 近期健康记录</div> |
|
|
<div class="timeline-title">📅 近期健康记录</div> |
|
|
<div class="timeline-list"> |
|
|
<div class="timeline-list"> |
|
|
<div v-for="record in selectedCamel.healthRecords" :key="record.date" class="timeline-item"> |
|
|
<div v-for="record in selectedCamel.healthRecords" :key="record.date" class="timeline-item"> |
|
|
@ -244,7 +250,6 @@ |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 健康建议 --> |
|
|
|
|
|
<div class="card advice-card"> |
|
|
<div class="card advice-card"> |
|
|
<div class="card-header"> |
|
|
<div class="card-header"> |
|
|
<span class="title">💡 健康管理建议</span> |
|
|
<span class="title">💡 健康管理建议</span> |
|
|
@ -266,14 +271,12 @@ |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
import * as echarts from 'echarts'; |
|
|
import * as echarts from 'echarts'; |
|
|
import dayjs from 'dayjs'; |
|
|
|
|
|
import 'dayjs/locale/zh-cn'; |
|
|
|
|
|
|
|
|
|
|
|
export default { |
|
|
export default { |
|
|
name: 'HealthDashboard', |
|
|
name: 'HealthDashboard', |
|
|
|
|
|
|
|
|
data() { |
|
|
data() { |
|
|
return { |
|
|
return { |
|
|
// 驼圈数据 (根据Excel) |
|
|
|
|
|
pens: [ |
|
|
pens: [ |
|
|
{ name: 'A1圈', count: 35 }, { name: 'A2圈', count: 35 }, |
|
|
{ name: 'A1圈', count: 35 }, { name: 'A2圈', count: 35 }, |
|
|
{ name: 'B1圈', count: 35 }, { name: 'B2圈', count: 35 }, |
|
|
{ name: 'B1圈', count: 35 }, { name: 'B2圈', count: 35 }, |
|
|
@ -288,7 +291,6 @@ export default { |
|
|
], |
|
|
], |
|
|
selectedPen: 'A1圈', |
|
|
selectedPen: 'A1圈', |
|
|
|
|
|
|
|
|
// 健康统计数据 |
|
|
|
|
|
healthStats: { |
|
|
healthStats: { |
|
|
totalCamels: 700, |
|
|
totalCamels: 700, |
|
|
healthyRate: 94.2, |
|
|
healthyRate: 94.2, |
|
|
@ -296,7 +298,6 @@ export default { |
|
|
warningCount: 42 |
|
|
warningCount: 42 |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// 健康预警 |
|
|
|
|
|
healthAlerts: [ |
|
|
healthAlerts: [ |
|
|
{ level: 'high', levelText: '紧急', title: 'A1圈骆驼体温异常', desc: '编号A1-023体温39.2°C', time: '09:32' }, |
|
|
{ level: 'high', levelText: '紧急', title: 'A1圈骆驼体温异常', desc: '编号A1-023体温39.2°C', time: '09:32' }, |
|
|
{ level: 'medium', levelText: '警告', title: 'B2圈空气质量下降', desc: '氨气浓度超标', time: '09:15' }, |
|
|
{ level: 'medium', levelText: '警告', title: 'B2圈空气质量下降', desc: '氨气浓度超标', time: '09:15' }, |
|
|
@ -304,7 +305,6 @@ export default { |
|
|
{ level: 'medium', levelText: '警告', title: 'E2圈饮水异常', desc: '2峰骆驼饮水次数减少', time: '07:22' } |
|
|
{ level: 'medium', levelText: '警告', title: 'E2圈饮水异常', desc: '2峰骆驼饮水次数减少', time: '07:22' } |
|
|
], |
|
|
], |
|
|
|
|
|
|
|
|
// 圈舍健康数据 |
|
|
|
|
|
penHealthData: { |
|
|
penHealthData: { |
|
|
avgTemp: 37.5, |
|
|
avgTemp: 37.5, |
|
|
airQuality: 68, |
|
|
airQuality: 68, |
|
|
@ -312,7 +312,6 @@ export default { |
|
|
ventilation: 3 |
|
|
ventilation: 3 |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// 图表配置 |
|
|
|
|
|
chartMetrics: [ |
|
|
chartMetrics: [ |
|
|
{ key: 'temperature', name: '体温' }, |
|
|
{ key: 'temperature', name: '体温' }, |
|
|
{ key: 'activity', name: '活动量' }, |
|
|
{ key: 'activity', name: '活动量' }, |
|
|
@ -320,19 +319,16 @@ export default { |
|
|
], |
|
|
], |
|
|
activeMetric: 'temperature', |
|
|
activeMetric: 'temperature', |
|
|
|
|
|
|
|
|
// 骆驼数据 (每个圈舍35峰骆驼) |
|
|
|
|
|
camels: [], |
|
|
camels: [], |
|
|
camelSearch: '', |
|
|
camelSearch: '', |
|
|
selectedCamel: null, |
|
|
selectedCamel: null, |
|
|
|
|
|
|
|
|
// 趋势数据 |
|
|
|
|
|
trendData: { |
|
|
trendData: { |
|
|
temperature: [37.2, 37.3, 37.4, 37.5, 37.6, 37.5, 37.4], |
|
|
temperature: [37.2, 37.3, 37.4, 37.5, 37.6, 37.5, 37.4], |
|
|
activity: [5200, 5400, 5600, 5800, 5900, 6100, 6000], |
|
|
activity: [5200, 5400, 5600, 5800, 5900, 6100, 6000], |
|
|
healthRate: [92.5, 93.1, 93.6, 93.8, 94.0, 94.1, 94.2] |
|
|
healthRate: [92.5, 93.1, 93.6, 93.8, 94.0, 94.1, 94.2] |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
// 健康建议 |
|
|
|
|
|
healthAdvice: [ |
|
|
healthAdvice: [ |
|
|
{ icon: '🌡️', title: '体温监控', desc: 'A1圈有2峰骆驼体温偏高,建议隔离观察' }, |
|
|
{ icon: '🌡️', title: '体温监控', desc: 'A1圈有2峰骆驼体温偏高,建议隔离观察' }, |
|
|
{ icon: '💨', title: '通风改善', desc: 'B2圈空气质量下降,建议开启通风设备' }, |
|
|
{ icon: '💨', title: '通风改善', desc: 'B2圈空气质量下降,建议开启通风设备' }, |
|
|
@ -340,11 +336,9 @@ export default { |
|
|
{ icon: '🏃', title: '活动促进', desc: 'C1圈活动量偏低,建议增加驱赶活动' } |
|
|
{ icon: '🏃', title: '活动促进', desc: 'C1圈活动量偏低,建议增加驱赶活动' } |
|
|
], |
|
|
], |
|
|
|
|
|
|
|
|
// ECharts实例 |
|
|
|
|
|
healthChartIns: null, |
|
|
healthChartIns: null, |
|
|
|
|
|
|
|
|
// 时间标签 |
|
|
|
|
|
timeLabels: [] |
|
|
|
|
|
|
|
|
timeLabels: [], |
|
|
|
|
|
resizeObserver: null |
|
|
}; |
|
|
}; |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
@ -361,31 +355,20 @@ export default { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
mounted() { |
|
|
mounted() { |
|
|
this.getCurrentDateTime(); |
|
|
|
|
|
this.initTimeLabels(); |
|
|
this.initTimeLabels(); |
|
|
this.initCamelData(); |
|
|
this.initCamelData(); |
|
|
this.initHealthChart(); |
|
|
this.initHealthChart(); |
|
|
window.addEventListener('resize', this.handleResize); |
|
|
window.addEventListener('resize', this.handleResize); |
|
|
|
|
|
this.observeChartResize(); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
beforeDestroy() { |
|
|
beforeDestroy() { |
|
|
if (this.healthChartIns) this.healthChartIns.dispose(); |
|
|
if (this.healthChartIns) this.healthChartIns.dispose(); |
|
|
|
|
|
if (this.resizeObserver) this.resizeObserver.disconnect(); |
|
|
window.removeEventListener('resize', this.handleResize); |
|
|
window.removeEventListener('resize', this.handleResize); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
methods: { |
|
|
methods: { |
|
|
getCurrentDateTime() { |
|
|
|
|
|
const dateDom = document.querySelector('#cloudDate'); |
|
|
|
|
|
const timeDom = document.querySelector('#cloudTime'); |
|
|
|
|
|
const update = () => { |
|
|
|
|
|
const now = dayjs().locale('zh-cn'); |
|
|
|
|
|
if (dateDom) dateDom.innerHTML = now.format('MM月DD日 dddd'); |
|
|
|
|
|
if (timeDom) timeDom.innerHTML = now.format('HH:mm:ss'); |
|
|
|
|
|
}; |
|
|
|
|
|
update(); |
|
|
|
|
|
setInterval(update, 1000); |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
initTimeLabels() { |
|
|
initTimeLabels() { |
|
|
for (let i = 6; i >= 0; i--) { |
|
|
for (let i = 6; i >= 0; i--) { |
|
|
this.timeLabels.push(dayjs().subtract(i, 'day').format('MM/DD')); |
|
|
this.timeLabels.push(dayjs().subtract(i, 'day').format('MM/DD')); |
|
|
@ -393,7 +376,6 @@ export default { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
initCamelData() { |
|
|
initCamelData() { |
|
|
// 为每个圈舍生成35峰骆驼数据 |
|
|
|
|
|
const allCamels = []; |
|
|
const allCamels = []; |
|
|
const statuses = ['healthy', 'warning', 'critical']; |
|
|
const statuses = ['healthy', 'warning', 'critical']; |
|
|
const statusWeights = [0.85, 0.12, 0.03]; |
|
|
const statusWeights = [0.85, 0.12, 0.03]; |
|
|
@ -430,8 +412,8 @@ export default { |
|
|
|
|
|
|
|
|
generateHealthRecords(status) { |
|
|
generateHealthRecords(status) { |
|
|
const records = []; |
|
|
const records = []; |
|
|
const dates = [0, 1, 2, 3, 4, 5, 6]; |
|
|
|
|
|
for (let i = 0; i < 5; i++) { |
|
|
|
|
|
|
|
|
const dates = [0, 1, 2, 3]; |
|
|
|
|
|
for (let i = 0; i < 4; i++) { |
|
|
const date = dayjs().subtract(dates[i], 'day').format('MM/DD'); |
|
|
const date = dayjs().subtract(dates[i], 'day').format('MM/DD'); |
|
|
if (status === 'healthy') { |
|
|
if (status === 'healthy') { |
|
|
records.push({ |
|
|
records.push({ |
|
|
@ -459,7 +441,6 @@ export default { |
|
|
selectPen(penName) { |
|
|
selectPen(penName) { |
|
|
this.selectedPen = penName; |
|
|
this.selectedPen = penName; |
|
|
this.selectedCamel = null; |
|
|
this.selectedCamel = null; |
|
|
// 更新圈舍健康数据 (模拟) |
|
|
|
|
|
this.penHealthData = { |
|
|
this.penHealthData = { |
|
|
avgTemp: +(37.2 + Math.random() * 0.8).toFixed(1), |
|
|
avgTemp: +(37.2 + Math.random() * 0.8).toFixed(1), |
|
|
airQuality: Math.floor(40 + Math.random() * 50), |
|
|
airQuality: Math.floor(40 + Math.random() * 50), |
|
|
@ -478,11 +459,7 @@ export default { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
getHealthStatusText(status) { |
|
|
getHealthStatusText(status) { |
|
|
const map = { |
|
|
|
|
|
healthy: '健康', |
|
|
|
|
|
warning: '亚健康', |
|
|
|
|
|
critical: '异常' |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const map = { healthy: '健康', warning: '亚健康', critical: '异常' }; |
|
|
return map[status] || '未知'; |
|
|
return map[status] || '未知'; |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
@ -529,22 +506,15 @@ export default { |
|
|
|
|
|
|
|
|
initHealthChart() { |
|
|
initHealthChart() { |
|
|
const chartDom = this.$refs.healthChart; |
|
|
const chartDom = this.$refs.healthChart; |
|
|
|
|
|
if (!chartDom) return; |
|
|
this.healthChartIns = echarts.init(chartDom); |
|
|
this.healthChartIns = echarts.init(chartDom); |
|
|
this.updateHealthChart(); |
|
|
this.updateHealthChart(); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
updateHealthChart() { |
|
|
updateHealthChart() { |
|
|
const currentData = this.trendData[this.activeMetric]; |
|
|
const currentData = this.trendData[this.activeMetric]; |
|
|
const unitMap = { |
|
|
|
|
|
temperature: '°C', |
|
|
|
|
|
activity: '步', |
|
|
|
|
|
healthRate: '%' |
|
|
|
|
|
}; |
|
|
|
|
|
const nameMap = { |
|
|
|
|
|
temperature: '平均体温', |
|
|
|
|
|
activity: '平均活动量', |
|
|
|
|
|
healthRate: '健康率' |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const unitMap = { temperature: '°C', activity: '步', healthRate: '%' }; |
|
|
|
|
|
const nameMap = { temperature: '平均体温', activity: '平均活动量', healthRate: '健康率' }; |
|
|
const yAxisMin = this.activeMetric === 'healthRate' ? 90 : null; |
|
|
const yAxisMin = this.activeMetric === 'healthRate' ? 90 : null; |
|
|
|
|
|
|
|
|
this.healthChartIns.setOption({ |
|
|
this.healthChartIns.setOption({ |
|
|
@ -590,7 +560,16 @@ export default { |
|
|
this.updateHealthChart(); |
|
|
this.updateHealthChart(); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
observeChartResize() { |
|
|
|
|
|
const chartEl = this.$refs.healthChart; |
|
|
|
|
|
if (!chartEl) return; |
|
|
|
|
|
this.resizeObserver = new ResizeObserver(() => { |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
if (this.healthChartIns) this.healthChartIns.resize(); |
|
|
|
|
|
}, 100); |
|
|
|
|
|
}); |
|
|
|
|
|
this.resizeObserver.observe(chartEl); |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
handleResize() { |
|
|
handleResize() { |
|
|
if (this.healthChartIns) this.healthChartIns.resize(); |
|
|
if (this.healthChartIns) this.healthChartIns.resize(); |
|
|
@ -602,56 +581,65 @@ export default { |
|
|
<style scoped lang="scss"> |
|
|
<style scoped lang="scss"> |
|
|
.health-dashboard { |
|
|
.health-dashboard { |
|
|
width: 100%; |
|
|
width: 100%; |
|
|
height: calc(100vh - 100px); |
|
|
|
|
|
|
|
|
height: calc(100vh - 120px); |
|
|
background: transparent; |
|
|
background: transparent; |
|
|
padding: 20px 24px 30px; |
|
|
|
|
|
|
|
|
padding: 20px 20px 0; |
|
|
box-sizing: border-box; |
|
|
box-sizing: border-box; |
|
|
font-family: 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', sans-serif; |
|
|
font-family: 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', sans-serif; |
|
|
overflow-y: auto; |
|
|
|
|
|
overflow-x: auto; |
|
|
|
|
|
|
|
|
overflow: hidden; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.dashboard-main { |
|
|
.dashboard-main { |
|
|
display: flex; |
|
|
|
|
|
|
|
|
display: grid; |
|
|
|
|
|
grid-template-columns: 1.2fr 2fr 1.2fr; |
|
|
gap: 20px; |
|
|
gap: 20px; |
|
|
flex-wrap: wrap; |
|
|
|
|
|
align-items: stretch; |
|
|
|
|
|
|
|
|
|
|
|
.col-left, .col-right { |
|
|
|
|
|
flex: 1.1; |
|
|
|
|
|
min-width: 280px; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
gap: 20px; |
|
|
|
|
|
} |
|
|
|
|
|
.col-center { |
|
|
|
|
|
flex: 1.8; |
|
|
|
|
|
min-width: 420px; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
gap: 20px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
height: 100%; |
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.col-left { |
|
|
|
|
|
display: grid; |
|
|
|
|
|
grid-template-rows: 1fr auto 1fr; |
|
|
|
|
|
gap: 20px; |
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
overflow: hidden; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.col-center { |
|
|
|
|
|
display: grid; |
|
|
|
|
|
grid-template-rows: auto 0.8fr 1fr; |
|
|
|
|
|
gap: 20px; |
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
overflow: hidden; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.col-right { |
|
|
|
|
|
display: grid; |
|
|
|
|
|
grid-template-rows: 1fr auto; |
|
|
|
|
|
gap: 20px; |
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
overflow: hidden; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.card { |
|
|
.card { |
|
|
background: transparent; |
|
|
|
|
|
backdrop-filter: blur(0); |
|
|
|
|
|
|
|
|
background: rgba(8, 28, 45, 0.55); |
|
|
|
|
|
backdrop-filter: blur(3px); |
|
|
border-radius: 16px; |
|
|
border-radius: 16px; |
|
|
border: 1.5px solid rgba(52, 152, 219, 0.7); |
|
|
border: 1.5px solid rgba(52, 152, 219, 0.7); |
|
|
overflow: hidden; |
|
|
|
|
|
transition: all 0.3s ease; |
|
|
transition: all 0.3s ease; |
|
|
display: flex; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
flex-direction: column; |
|
|
|
|
|
|
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
overflow: hidden; |
|
|
|
|
|
|
|
|
&:hover { |
|
|
&:hover { |
|
|
border-color: #5dade2; |
|
|
border-color: #5dade2; |
|
|
box-shadow: 0 4px 20px rgba(52, 152, 219, 0.25); |
|
|
box-shadow: 0 4px 20px rgba(52, 152, 219, 0.25); |
|
|
background: rgba(52, 152, 219, 0.05); |
|
|
|
|
|
|
|
|
background: rgba(52, 152, 219, 0.08); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.card-header { |
|
|
.card-header { |
|
|
padding: 14px 20px; |
|
|
|
|
|
|
|
|
padding: 12px 16px; |
|
|
border-bottom: 1px solid rgba(52, 152, 219, 0.4); |
|
|
border-bottom: 1px solid rgba(52, 152, 219, 0.4); |
|
|
display: flex; |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
justify-content: space-between; |
|
|
@ -659,9 +647,9 @@ export default { |
|
|
flex-wrap: wrap; |
|
|
flex-wrap: wrap; |
|
|
gap: 10px; |
|
|
gap: 10px; |
|
|
flex-shrink: 0; |
|
|
flex-shrink: 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.title { |
|
|
.title { |
|
|
font-size: 16px; |
|
|
|
|
|
|
|
|
font-size: 15px; |
|
|
font-weight: 600; |
|
|
font-weight: 600; |
|
|
color: #fff; |
|
|
color: #fff; |
|
|
letter-spacing: 0.5px; |
|
|
letter-spacing: 0.5px; |
|
|
@ -700,8 +688,13 @@ export default { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 驼圈列表 |
|
|
|
|
|
|
|
|
// 驼圈列表 - 带滚动条 |
|
|
.pen-list-card { |
|
|
.pen-list-card { |
|
|
|
|
|
.pen-grid-wrapper { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
overflow-y: auto; |
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
} |
|
|
.pen-grid { |
|
|
.pen-grid { |
|
|
display: grid; |
|
|
display: grid; |
|
|
grid-template-columns: repeat(4, 1fr); |
|
|
grid-template-columns: repeat(4, 1fr); |
|
|
@ -736,19 +729,20 @@ export default { |
|
|
|
|
|
|
|
|
// 统计卡片 |
|
|
// 统计卡片 |
|
|
.stats-card { |
|
|
.stats-card { |
|
|
|
|
|
flex-shrink: 0; |
|
|
.stats-grid { |
|
|
.stats-grid { |
|
|
display: grid; |
|
|
display: grid; |
|
|
grid-template-columns: repeat(2, 1fr); |
|
|
grid-template-columns: repeat(2, 1fr); |
|
|
gap: 16px; |
|
|
gap: 16px; |
|
|
padding: 20px; |
|
|
|
|
|
|
|
|
padding: 16px; |
|
|
.stat-item { |
|
|
.stat-item { |
|
|
text-align: center; |
|
|
text-align: center; |
|
|
.stat-value { |
|
|
.stat-value { |
|
|
font-size: 28px; |
|
|
|
|
|
|
|
|
font-size: 24px; |
|
|
font-weight: bold; |
|
|
font-weight: bold; |
|
|
color: #5dade2; |
|
|
color: #5dade2; |
|
|
.unit { |
|
|
.unit { |
|
|
font-size: 12px; |
|
|
|
|
|
|
|
|
font-size: 11px; |
|
|
margin-left: 2px; |
|
|
margin-left: 2px; |
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
} |
|
|
} |
|
|
@ -767,57 +761,49 @@ export default { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 预警列表 |
|
|
|
|
|
|
|
|
// 预警列表 - 文字放大 |
|
|
.alert-card { |
|
|
.alert-card { |
|
|
flex: 1; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
|
|
|
|
|
|
.alert-list { |
|
|
|
|
|
padding: 8px 16px; |
|
|
|
|
|
|
|
|
.alert-list-wrapper { |
|
|
flex: 1; |
|
|
flex: 1; |
|
|
overflow-y: auto; |
|
|
overflow-y: auto; |
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
} |
|
|
|
|
|
.alert-list { |
|
|
|
|
|
padding: 12px 16px; |
|
|
.alert-item { |
|
|
.alert-item { |
|
|
display: flex; |
|
|
display: flex; |
|
|
align-items: flex-start; |
|
|
align-items: flex-start; |
|
|
gap: 12px; |
|
|
gap: 12px; |
|
|
border-bottom: 1px solid rgba(52, 152, 219, 0.2); |
|
|
border-bottom: 1px solid rgba(52, 152, 219, 0.2); |
|
|
padding: 12px 4px; |
|
|
|
|
|
|
|
|
padding: 14px 4px; |
|
|
.alert-level { |
|
|
.alert-level { |
|
|
font-size: 10px; |
|
|
|
|
|
padding: 2px 8px; |
|
|
|
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
padding: 4px 12px; |
|
|
border-radius: 20px; |
|
|
border-radius: 20px; |
|
|
min-width: 40px; |
|
|
|
|
|
|
|
|
min-width: 48px; |
|
|
text-align: center; |
|
|
text-align: center; |
|
|
&.high { |
|
|
|
|
|
background: rgba(231, 76, 60, 0.2); |
|
|
|
|
|
color: #e74c3c; |
|
|
|
|
|
} |
|
|
|
|
|
&.medium { |
|
|
|
|
|
background: rgba(243, 156, 18, 0.2); |
|
|
|
|
|
color: #f39c12; |
|
|
|
|
|
} |
|
|
|
|
|
&.low { |
|
|
|
|
|
background: rgba(52, 152, 219, 0.2); |
|
|
|
|
|
color: #3498db; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
&.high { background: rgba(231, 76, 60, 0.2); color: #e74c3c; } |
|
|
|
|
|
&.medium { background: rgba(243, 156, 18, 0.2); color: #f39c12; } |
|
|
|
|
|
&.low { background: rgba(52, 152, 219, 0.2); color: #3498db; } |
|
|
} |
|
|
} |
|
|
.alert-content { |
|
|
.alert-content { |
|
|
flex: 1; |
|
|
flex: 1; |
|
|
.alert-title { |
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.9); |
|
|
|
|
|
|
|
|
.alert-title { |
|
|
|
|
|
font-size: 14px; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.95); |
|
|
|
|
|
margin-bottom: 4px; |
|
|
} |
|
|
} |
|
|
.alert-desc { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
|
|
|
margin-top: 2px; |
|
|
|
|
|
|
|
|
.alert-desc { |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.7); |
|
|
|
|
|
line-height: 1.4; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
.alert-time { |
|
|
|
|
|
font-size: 10px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.4); |
|
|
|
|
|
|
|
|
.alert-time { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.5); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -825,53 +811,35 @@ export default { |
|
|
|
|
|
|
|
|
// 圈舍详情 |
|
|
// 圈舍详情 |
|
|
.pen-detail-card { |
|
|
.pen-detail-card { |
|
|
|
|
|
flex-shrink: 0; |
|
|
.pen-detail { |
|
|
.pen-detail { |
|
|
display: grid; |
|
|
display: grid; |
|
|
grid-template-columns: repeat(4, 1fr); |
|
|
grid-template-columns: repeat(4, 1fr); |
|
|
gap: 16px; |
|
|
gap: 16px; |
|
|
padding: 20px; |
|
|
|
|
|
|
|
|
padding: 16px; |
|
|
.detail-item { |
|
|
.detail-item { |
|
|
text-align: center; |
|
|
text-align: center; |
|
|
.detail-label { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.7); |
|
|
|
|
|
margin-bottom: 6px; |
|
|
|
|
|
} |
|
|
|
|
|
.detail-value { |
|
|
|
|
|
font-size: 22px; |
|
|
|
|
|
font-weight: bold; |
|
|
|
|
|
color: #5dade2; |
|
|
|
|
|
.unit { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
margin-left: 2px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.5); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
.detail-status { |
|
|
|
|
|
font-size: 10px; |
|
|
|
|
|
margin-top: 4px; |
|
|
|
|
|
&.normal { color: #2ecc71; } |
|
|
|
|
|
&.warning { color: #f39c12; } |
|
|
|
|
|
&.danger { color: #e74c3c; } |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
.detail-label { font-size: 11px; color: rgba(255, 255, 255, 0.7); margin-bottom: 6px; } |
|
|
|
|
|
.detail-value { font-size: 20px; font-weight: bold; color: #5dade2; .unit { font-size: 10px; } } |
|
|
|
|
|
.detail-status { font-size: 10px; margin-top: 4px; &.normal { color: #2ecc71; } &.warning { color: #f39c12; } &.danger { color: #e74c3c; } } |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 图表容器 |
|
|
// 图表容器 |
|
|
|
|
|
.chart-card { |
|
|
|
|
|
flex-shrink: 0; |
|
|
|
|
|
} |
|
|
.chart-container { |
|
|
.chart-container { |
|
|
width: 100%; |
|
|
width: 100%; |
|
|
height: 240px; |
|
|
|
|
|
padding: 8px; |
|
|
|
|
|
|
|
|
height: 100%; |
|
|
|
|
|
min-height: 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 骆驼列表 |
|
|
// 骆驼列表 |
|
|
.camel-list-card { |
|
|
.camel-list-card { |
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
|
|
|
|
|
|
.camel-search { |
|
|
.camel-search { |
|
|
padding: 12px 16px; |
|
|
|
|
|
|
|
|
padding: 10px 16px; |
|
|
flex-shrink: 0; |
|
|
flex-shrink: 0; |
|
|
.search-input { |
|
|
.search-input { |
|
|
width: 100%; |
|
|
width: 100%; |
|
|
@ -882,22 +850,20 @@ export default { |
|
|
color: white; |
|
|
color: white; |
|
|
font-size: 12px; |
|
|
font-size: 12px; |
|
|
outline: none; |
|
|
outline: none; |
|
|
&:focus { |
|
|
|
|
|
border-color: #5dade2; |
|
|
|
|
|
} |
|
|
|
|
|
&::placeholder { |
|
|
|
|
|
color: rgba(255, 255, 255, 0.4); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
&:focus { border-color: #5dade2; } |
|
|
|
|
|
&::placeholder { color: rgba(255, 255, 255, 0.4); } |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
.camel-grid-wrapper { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
overflow-y: auto; |
|
|
|
|
|
min-height: 0; |
|
|
|
|
|
} |
|
|
.camel-grid { |
|
|
.camel-grid { |
|
|
display: grid; |
|
|
display: grid; |
|
|
grid-template-columns: repeat(3, 1fr); |
|
|
|
|
|
gap: 12px; |
|
|
|
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); |
|
|
|
|
|
gap: 10px; |
|
|
padding: 16px; |
|
|
padding: 16px; |
|
|
flex: 1; |
|
|
|
|
|
overflow-y: auto; |
|
|
|
|
|
max-height: 280px; |
|
|
|
|
|
.camel-item { |
|
|
.camel-item { |
|
|
background: rgba(0, 0, 0, 0.3); |
|
|
background: rgba(0, 0, 0, 0.3); |
|
|
border: 1px solid rgba(52, 152, 219, 0.4); |
|
|
border: 1px solid rgba(52, 152, 219, 0.4); |
|
|
@ -908,247 +874,99 @@ export default { |
|
|
gap: 10px; |
|
|
gap: 10px; |
|
|
cursor: pointer; |
|
|
cursor: pointer; |
|
|
transition: all 0.2s; |
|
|
transition: all 0.2s; |
|
|
&:hover { |
|
|
|
|
|
border-color: #5dade2; |
|
|
|
|
|
background: rgba(52, 152, 219, 0.1); |
|
|
|
|
|
} |
|
|
|
|
|
&.warning { |
|
|
|
|
|
border-color: rgba(243, 156, 18, 0.6); |
|
|
|
|
|
} |
|
|
|
|
|
.camel-avatar { |
|
|
|
|
|
position: relative; |
|
|
|
|
|
.camel-icon { |
|
|
|
|
|
font-size: 28px; |
|
|
|
|
|
} |
|
|
|
|
|
.warning-dot { |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
top: -2px; |
|
|
|
|
|
right: -2px; |
|
|
|
|
|
width: 10px; |
|
|
|
|
|
height: 10px; |
|
|
|
|
|
background: #f39c12; |
|
|
|
|
|
border-radius: 50%; |
|
|
|
|
|
animation: pulse 1.5s infinite; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
.camel-info { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
.camel-id { |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
} |
|
|
|
|
|
.camel-temp { |
|
|
|
|
|
font-size: 10px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
.camel-status { |
|
|
|
|
|
font-size: 10px; |
|
|
|
|
|
padding: 2px 6px; |
|
|
|
|
|
border-radius: 10px; |
|
|
|
|
|
&.healthy { |
|
|
|
|
|
background: rgba(46, 204, 113, 0.2); |
|
|
|
|
|
color: #2ecc71; |
|
|
|
|
|
} |
|
|
|
|
|
&.warning { |
|
|
|
|
|
background: rgba(243, 156, 18, 0.2); |
|
|
|
|
|
color: #f39c12; |
|
|
|
|
|
} |
|
|
|
|
|
&.critical { |
|
|
|
|
|
background: rgba(231, 76, 60, 0.2); |
|
|
|
|
|
color: #e74c3c; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
&:hover { border-color: #5dade2; background: rgba(52, 152, 219, 0.1); } |
|
|
|
|
|
&.warning { border-color: rgba(243, 156, 18, 0.6); } |
|
|
|
|
|
.camel-avatar { position: relative; .camel-icon { font-size: 28px; } .warning-dot { position: absolute; top: -2px; right: -2px; width: 10px; height: 10px; background: #f39c12; border-radius: 50%; animation: pulse 1.5s infinite; } } |
|
|
|
|
|
.camel-info { flex: 1; .camel-id { font-size: 12px; font-weight: 500; color: #fff; } .camel-temp { font-size: 10px; color: rgba(255, 255, 255, 0.6); } } |
|
|
|
|
|
.camel-status { font-size: 10px; padding: 2px 6px; border-radius: 10px; &.healthy { background: rgba(46, 204, 113, 0.2); color: #2ecc71; } &.warning { background: rgba(243, 156, 18, 0.2); color: #f39c12; } &.critical { background: rgba(231, 76, 60, 0.2); color: #e74c3c; } } |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 健康报告 |
|
|
|
|
|
|
|
|
// 健康报告 - 近期健康记录文字放大 |
|
|
.health-report-card { |
|
|
.health-report-card { |
|
|
flex: 1; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
|
|
|
|
|
|
.health-report { |
|
|
.health-report { |
|
|
flex: 1; |
|
|
flex: 1; |
|
|
display: flex; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
flex-direction: column; |
|
|
|
|
|
|
|
|
|
|
|
min-height: 0; |
|
|
.report-header { |
|
|
.report-header { |
|
|
display: flex; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
gap: 16px; |
|
|
gap: 16px; |
|
|
padding: 20px; |
|
|
|
|
|
|
|
|
padding: 16px; |
|
|
background: rgba(0, 0, 0, 0.2); |
|
|
background: rgba(0, 0, 0, 0.2); |
|
|
border-bottom: 1px solid rgba(52, 152, 219, 0.3); |
|
|
border-bottom: 1px solid rgba(52, 152, 219, 0.3); |
|
|
flex-shrink: 0; |
|
|
flex-shrink: 0; |
|
|
.camel-avatar-large { |
|
|
|
|
|
font-size: 48px; |
|
|
|
|
|
} |
|
|
|
|
|
.camel-basic { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
.camel-name { |
|
|
|
|
|
font-size: 18px; |
|
|
|
|
|
font-weight: bold; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
} |
|
|
|
|
|
.camel-location { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
|
|
|
margin-top: 4px; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
.health-badge { |
|
|
|
|
|
padding: 4px 12px; |
|
|
|
|
|
border-radius: 20px; |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
&.healthy { |
|
|
|
|
|
background: rgba(46, 204, 113, 0.2); |
|
|
|
|
|
color: #2ecc71; |
|
|
|
|
|
} |
|
|
|
|
|
&.warning { |
|
|
|
|
|
background: rgba(243, 156, 18, 0.2); |
|
|
|
|
|
color: #f39c12; |
|
|
|
|
|
} |
|
|
|
|
|
&.critical { |
|
|
|
|
|
background: rgba(231, 76, 60, 0.2); |
|
|
|
|
|
color: #e74c3c; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
.camel-avatar-large { font-size: 48px; } |
|
|
|
|
|
.camel-basic { flex: 1; .camel-name { font-size: 16px; font-weight: bold; color: #fff; } .camel-location { font-size: 11px; color: rgba(255, 255, 255, 0.6); margin-top: 4px; } } |
|
|
|
|
|
.health-badge { padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: 500; &.healthy { background: rgba(46, 204, 113, 0.2); color: #2ecc71; } &.warning { background: rgba(243, 156, 18, 0.2); color: #f39c12; } &.critical { background: rgba(231, 76, 60, 0.2); color: #e74c3c; } } |
|
|
} |
|
|
} |
|
|
.report-metrics { |
|
|
.report-metrics { |
|
|
padding: 16px; |
|
|
padding: 16px; |
|
|
flex-shrink: 0; |
|
|
flex-shrink: 0; |
|
|
.metric-row { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
gap: 20px; |
|
|
|
|
|
margin-bottom: 16px; |
|
|
|
|
|
.metric { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
.metric-label { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
|
|
|
margin-bottom: 4px; |
|
|
|
|
|
} |
|
|
|
|
|
.metric-value { |
|
|
|
|
|
font-size: 20px; |
|
|
|
|
|
font-weight: bold; |
|
|
|
|
|
color: #5dade2; |
|
|
|
|
|
&.warning { |
|
|
|
|
|
color: #e74c3c; |
|
|
|
|
|
} |
|
|
|
|
|
.unit { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
margin-left: 2px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.5); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
.metric-range { |
|
|
|
|
|
font-size: 10px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.4); |
|
|
|
|
|
margin-top: 4px; |
|
|
|
|
|
} |
|
|
|
|
|
.metric-progress { |
|
|
|
|
|
margin-top: 8px; |
|
|
|
|
|
background: rgba(52, 152, 219, 0.3); |
|
|
|
|
|
border-radius: 10px; |
|
|
|
|
|
height: 6px; |
|
|
|
|
|
.progress-bar { |
|
|
|
|
|
height: 6px; |
|
|
|
|
|
background: #5dade2; |
|
|
|
|
|
border-radius: 10px; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
.metric-row { display: flex; gap: 16px; margin-bottom: 16px; .metric { flex: 1; .metric-label { font-size: 11px; color: rgba(255, 255, 255, 0.6); margin-bottom: 4px; } .metric-value { font-size: 18px; font-weight: bold; color: #5dade2; &.warning { color: #e74c3c; } .unit { font-size: 10px; } } .metric-range { font-size: 9px; color: rgba(255, 255, 255, 0.4); margin-top: 4px; } .metric-progress { margin-top: 8px; background: rgba(52, 152, 219, 0.3); border-radius: 10px; height: 6px; .progress-bar { height: 6px; background: #5dade2; border-radius: 10px; } } } } |
|
|
} |
|
|
} |
|
|
.report-timeline { |
|
|
|
|
|
padding: 16px; |
|
|
|
|
|
border-top: 1px solid rgba(52, 152, 219, 0.3); |
|
|
|
|
|
|
|
|
.report-timeline.compact-timeline { |
|
|
flex: 1; |
|
|
flex: 1; |
|
|
overflow-y: auto; |
|
|
overflow-y: auto; |
|
|
.timeline-title { |
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
margin-bottom: 12px; |
|
|
|
|
|
} |
|
|
|
|
|
.timeline-list { |
|
|
|
|
|
.timeline-item { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
gap: 12px; |
|
|
|
|
|
margin-bottom: 12px; |
|
|
|
|
|
.timeline-date { |
|
|
|
|
|
font-size: 10px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.5); |
|
|
|
|
|
min-width: 50px; |
|
|
|
|
|
|
|
|
padding: 16px; |
|
|
|
|
|
border-top: 1px solid rgba(52, 152, 219, 0.3); |
|
|
|
|
|
.timeline-title { |
|
|
|
|
|
font-size: 14px; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
margin-bottom: 12px; |
|
|
|
|
|
} |
|
|
|
|
|
.timeline-list .timeline-item { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
gap: 16px; |
|
|
|
|
|
margin-bottom: 14px; |
|
|
|
|
|
padding-bottom: 10px; |
|
|
|
|
|
border-bottom: 1px solid rgba(52, 152, 219, 0.15); |
|
|
|
|
|
&:last-child { border-bottom: none; } |
|
|
|
|
|
.timeline-date { |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
|
|
|
min-width: 55px; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
} |
|
|
|
|
|
.timeline-content { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
.timeline-event { |
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.95); |
|
|
|
|
|
margin-bottom: 4px; |
|
|
} |
|
|
} |
|
|
.timeline-content { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
.timeline-event { |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.9); |
|
|
|
|
|
} |
|
|
|
|
|
.timeline-detail { |
|
|
|
|
|
font-size: 10px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.5); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
.timeline-detail { |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.65); |
|
|
|
|
|
line-height: 1.4; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
.no-selection { |
|
|
.no-selection { |
|
|
text-align: center; |
|
|
|
|
|
padding: 60px 20px; |
|
|
|
|
|
flex: 1; |
|
|
flex: 1; |
|
|
display: flex; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
flex-direction: column; |
|
|
justify-content: center; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
.placeholder-icon { |
|
|
|
|
|
font-size: 64px; |
|
|
|
|
|
opacity: 0.5; |
|
|
|
|
|
display: block; |
|
|
|
|
|
margin-bottom: 16px; |
|
|
|
|
|
} |
|
|
|
|
|
p { |
|
|
|
|
|
color: rgba(255, 255, 255, 0.5); |
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
text-align: center; |
|
|
|
|
|
padding: 40px 20px; |
|
|
|
|
|
.placeholder-icon { font-size: 64px; opacity: 0.5; margin-bottom: 16px; } |
|
|
|
|
|
p { color: rgba(255, 255, 255, 0.5); font-size: 13px; } |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 健康建议 |
|
|
// 健康建议 |
|
|
.advice-card { |
|
|
.advice-card { |
|
|
|
|
|
flex-shrink: 0; |
|
|
.advice-list { |
|
|
.advice-list { |
|
|
padding: 16px; |
|
|
padding: 16px; |
|
|
.advice-item { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
gap: 12px; |
|
|
|
|
|
margin-bottom: 16px; |
|
|
|
|
|
&:last-child { |
|
|
|
|
|
margin-bottom: 0; |
|
|
|
|
|
} |
|
|
|
|
|
.advice-icon { |
|
|
|
|
|
font-size: 20px; |
|
|
|
|
|
} |
|
|
|
|
|
.advice-content { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
.advice-title { |
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
} |
|
|
|
|
|
.advice-desc { |
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
color: rgba(255, 255, 255, 0.6); |
|
|
|
|
|
margin-top: 2px; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
.advice-item { display: flex; gap: 12px; margin-bottom: 16px; &:last-child { margin-bottom: 0; } .advice-icon { font-size: 20px; } .advice-content { flex: 1; .advice-title { font-size: 13px; font-weight: 500; color: #fff; } .advice-desc { font-size: 11px; color: rgba(255, 255, 255, 0.6); margin-top: 2px; } } } |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -1156,4 +974,26 @@ export default { |
|
|
0%, 100% { opacity: 1; transform: scale(1); } |
|
|
0%, 100% { opacity: 1; transform: scale(1); } |
|
|
50% { opacity: 0.5; transform: scale(1.2); } |
|
|
50% { opacity: 0.5; transform: scale(1.2); } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 滚动条美化 |
|
|
|
|
|
.pen-grid-wrapper::-webkit-scrollbar, |
|
|
|
|
|
.alert-list-wrapper::-webkit-scrollbar, |
|
|
|
|
|
.camel-grid-wrapper::-webkit-scrollbar, |
|
|
|
|
|
.report-timeline.compact-timeline::-webkit-scrollbar { |
|
|
|
|
|
width: 4px; |
|
|
|
|
|
} |
|
|
|
|
|
.pen-grid-wrapper::-webkit-scrollbar-track, |
|
|
|
|
|
.alert-list-wrapper::-webkit-scrollbar-track, |
|
|
|
|
|
.camel-grid-wrapper::-webkit-scrollbar-track, |
|
|
|
|
|
.report-timeline.compact-timeline::-webkit-scrollbar-track { |
|
|
|
|
|
background: rgba(52, 152, 219, 0.2); |
|
|
|
|
|
border-radius: 2px; |
|
|
|
|
|
} |
|
|
|
|
|
.pen-grid-wrapper::-webkit-scrollbar-thumb, |
|
|
|
|
|
.alert-list-wrapper::-webkit-scrollbar-thumb, |
|
|
|
|
|
.camel-grid-wrapper::-webkit-scrollbar-thumb, |
|
|
|
|
|
.report-timeline.compact-timeline::-webkit-scrollbar-thumb { |
|
|
|
|
|
background: #5dade2; |
|
|
|
|
|
border-radius: 2px; |
|
|
|
|
|
} |
|
|
</style> |
|
|
</style> |