feat: 用药提醒功能 + 移除医生相关页面
- 后端新增 GET /api/medications/reminders 接口 - 前端任务卡片区显示真实用药提醒 - 移除 DoctorListPage/DoctorChatPage 路由 - 移除"找医生"面板按钮 - 医生端另做 Web 页面
This commit is contained in:
@@ -15,6 +15,7 @@ class _TrendPageState extends ConsumerState<TrendPage> {
|
||||
int _period = 7;
|
||||
bool _showAllRecords = false;
|
||||
late List<Map<String, dynamic>> _data;
|
||||
final _chartKey = GlobalKey();
|
||||
|
||||
static const _labels = {
|
||||
'blood_pressure': '血压趋势',
|
||||
@@ -420,20 +421,24 @@ class _TrendPageState extends ConsumerState<TrendPage> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: CustomPaint(
|
||||
painter: _TrendChartPainter(
|
||||
data: _data,
|
||||
metricType: widget.metricType,
|
||||
isDualLine: _isDualLine,
|
||||
yMin: yRange.min,
|
||||
yMax: yRange.max,
|
||||
yStep: yRange.step,
|
||||
formatDateLabel: _formatDateLabel,
|
||||
formatValue: _formatValue,
|
||||
GestureDetector(
|
||||
key: _chartKey,
|
||||
onTapDown: (details) => _onChartTap(details, yRange),
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: CustomPaint(
|
||||
painter: _TrendChartPainter(
|
||||
data: _data,
|
||||
metricType: widget.metricType,
|
||||
isDualLine: _isDualLine,
|
||||
yMin: yRange.min,
|
||||
yMax: yRange.max,
|
||||
yStep: yRange.step,
|
||||
formatDateLabel: _formatDateLabel,
|
||||
formatValue: _formatValue,
|
||||
),
|
||||
size: Size.infinite,
|
||||
),
|
||||
size: Size.infinite,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -570,6 +575,101 @@ class _TrendPageState extends ConsumerState<TrendPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 图表点击检测 ====================
|
||||
void _onChartTap(TapDownDetails details, ({double min, double max, double step}) yRange) {
|
||||
final renderBox = _chartKey.currentContext?.findRenderObject() as RenderBox?;
|
||||
if (renderBox == null || _data.length < 2) return;
|
||||
|
||||
final localPosition = renderBox.globalToLocal(details.globalPosition);
|
||||
|
||||
const leftPadding = 44.0;
|
||||
const rightPadding = 8.0;
|
||||
final chartW = renderBox.size.width - leftPadding - rightPadding;
|
||||
|
||||
// 找到 x 方向最近的数据点
|
||||
int nearestIndex = 0;
|
||||
double minDist = double.infinity;
|
||||
for (int i = 0; i < _data.length; i++) {
|
||||
final pointX = leftPadding + (chartW * i / (_data.length - 1));
|
||||
final dist = (localPosition.dx - pointX).abs();
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 点击偏离数据点太远则不响应
|
||||
if (minDist > 40) return;
|
||||
|
||||
final item = _data[nearestIndex];
|
||||
final date = item['date'] as DateTime;
|
||||
final val = item['value'] as num;
|
||||
final val2 = item['value2'] as num?;
|
||||
final status = _getStatus(val, value2: val2);
|
||||
|
||||
String displayValue;
|
||||
if (_isDualLine) {
|
||||
displayValue = '${_formatValue(val)} / ${_formatValue(val2 ?? 0)} ${_getUnit()}';
|
||||
} else {
|
||||
displayValue = '${_formatValue(val)} ${_getUnit()}';
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: Text(
|
||||
_formatDateTime(date),
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
displayValue,
|
||||
style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Color(0xFF333333)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: _getStatusColor(status),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: _getStatusColor(status).withValues(alpha: 0.12),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
status,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _getStatusColor(status),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
child: const Text('关闭', style: TextStyle(color: Color(0xFF635BFF))),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
Reference in New Issue
Block a user