|
@@ -1,10 +1,8 @@
|
|
|
import { Component, OnInit } from '@angular/core';
|
|
|
-import{ChatPanelOptions,FmChatModalInput,FmodeChat,FmodeChatMessage,openChatPanelModal}from 'fmode-ng';
|
|
|
-import Parse from "parse";
|
|
|
-import{ModalController}from '@ionic/angular/standalone';
|
|
|
-import { CloudUser, CloudQuery } from 'src/lib/ncloud';
|
|
|
+import{ModalController, AlertController}from '@ionic/angular/standalone';
|
|
|
+import { CloudUser, CloudQuery, CloudObject } from 'src/lib/ncloud';
|
|
|
import { importTestData } from 'src/lib/import.data';
|
|
|
-
|
|
|
+import { AddDataModalComponent } from 'src/app/tab1/add-data-modal/add-data-modal.component';
|
|
|
@Component({
|
|
|
selector: 'app-tab1',
|
|
|
templateUrl: 'tab1.page.html',
|
|
@@ -18,15 +16,237 @@ export class Tab1Page implements OnInit{
|
|
|
heartRate: [] as any[],
|
|
|
loading: true
|
|
|
};
|
|
|
+ todayData = {
|
|
|
+ bloodGlucose: [] as any[],
|
|
|
+ bloodPressure: [] as any[],
|
|
|
+ heartRate: [] as any[],
|
|
|
+ loading: true}
|
|
|
+ // 更新 metrics 结构以包含时间和时间戳
|
|
|
+ metrics: Array<{
|
|
|
+ name: string;
|
|
|
+ value: string;
|
|
|
+ unit: string;
|
|
|
+ trend: string;
|
|
|
+ trendColor: string;
|
|
|
+ reference: string;
|
|
|
+ abnormal: boolean;
|
|
|
+ time: string;
|
|
|
+ timestamp: Date | null;
|
|
|
+}> = [
|
|
|
+ {
|
|
|
+ name: '血糖',
|
|
|
+ value: '--',
|
|
|
+ unit: 'mmol/L',
|
|
|
+ trend: 'caret-up',
|
|
|
+ trendColor: 'medium',
|
|
|
+ reference: '3.9-6.1',
|
|
|
+ abnormal: false,
|
|
|
+ time: '暂无数据',
|
|
|
+ timestamp: null
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '血压',
|
|
|
+ value: '--/--',
|
|
|
+ unit: 'mmHg',
|
|
|
+ trend: 'caret-down',
|
|
|
+ trendColor: 'medium',
|
|
|
+ reference: '120/80',
|
|
|
+ abnormal: false,
|
|
|
+ time: '暂无数据',
|
|
|
+ timestamp: null
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '心率',
|
|
|
+ value: '--',
|
|
|
+ unit: '次/分',
|
|
|
+ trend: 'caret-up',
|
|
|
+ trendColor: 'medium',
|
|
|
+ reference: '60-100',
|
|
|
+ abnormal: false,
|
|
|
+ time: '暂无数据',
|
|
|
+ timestamp: null
|
|
|
+ }
|
|
|
+];
|
|
|
constructor(
|
|
|
- private modalCtrl:ModalController
|
|
|
+ private modalCtrl:ModalController,
|
|
|
+ private alertCtrl:AlertController
|
|
|
){}
|
|
|
|
|
|
async ngOnInit() {
|
|
|
await this.loadHealthData();
|
|
|
+ await this.loadTodayData();
|
|
|
}
|
|
|
// 加载健康数据
|
|
|
+ async loadTodayData() {
|
|
|
+ try {
|
|
|
+ this.todayData.loading = true;
|
|
|
+
|
|
|
+ const user = new CloudUser();
|
|
|
+ const currentUser = await user.current();
|
|
|
+
|
|
|
+ if (!currentUser?.id) {
|
|
|
+ console.warn('未获取到当前用户');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取今天0点的时间
|
|
|
+ const today = new Date();
|
|
|
+ today.setHours(0, 0, 0, 0);
|
|
|
+
|
|
|
+ const [glucose, pressure, heartRate] = await Promise.all([
|
|
|
+ this.queryTodayData('BloodGlucose', currentUser, today),
|
|
|
+ this.queryTodayData('BloodPressure', currentUser, today),
|
|
|
+ this.queryTodayData('HeartRate', currentUser, today)
|
|
|
+ ]);
|
|
|
+
|
|
|
+ this.todayData = {
|
|
|
+ bloodGlucose: glucose,
|
|
|
+ bloodPressure: pressure,
|
|
|
+ heartRate: heartRate,
|
|
|
+ loading: false
|
|
|
+ };
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载今日数据失败:', error);
|
|
|
+ this.todayData.loading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async queryTodayData(className: string, user: CloudUser, date: Date): Promise<any[]> {
|
|
|
+ const query = new CloudQuery(className);
|
|
|
+ query.equalTo('patient', {
|
|
|
+ __type: 'Pointer',
|
|
|
+ className: '_User',
|
|
|
+ objectId: user.id
|
|
|
+ });
|
|
|
+ query.greaterThan('measurementTime', date);
|
|
|
+ query.descending('measurementTime'); // 按时间降序排列
|
|
|
+ return await query.find();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 打开添加数据模态框
|
|
|
+ async openAddDataModal() {
|
|
|
+ const modal = await this.modalCtrl.create({
|
|
|
+ component: AddDataModalComponent
|
|
|
+ });
|
|
|
+
|
|
|
+ await modal.present();
|
|
|
+
|
|
|
+ const { data } = await modal.onWillDismiss();
|
|
|
+ if (data) {
|
|
|
+ await this.saveHealthData(data.type, data.formData);
|
|
|
+ await this.loadTodayData(); // 重新加载今日数据
|
|
|
+ await this.loadHealthData(); // 重新加载所有健康数据
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存健康数据
|
|
|
+ async saveHealthData(type: string, formData: any) {
|
|
|
+ try {
|
|
|
+ const user = new CloudUser();
|
|
|
+ const currentUser = await user.current();
|
|
|
+
|
|
|
+ if (!currentUser?.id) {
|
|
|
+ console.warn('未获取到当前用户');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let className = '';
|
|
|
+ let dataObject: any = {
|
|
|
+ patient: {
|
|
|
+ __type: 'Pointer',
|
|
|
+ className: '_User',
|
|
|
+ objectId: currentUser.id
|
|
|
+ },
|
|
|
+ measurementTime: new Date()
|
|
|
+ };
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case 'bloodGlucose':
|
|
|
+ className = 'BloodGlucose';
|
|
|
+ dataObject.glucoseValue = parseFloat(formData.value);
|
|
|
+ dataObject.measurementType = formData.measurementType || '空腹';
|
|
|
+ if (formData.notes) dataObject.notes = formData.notes;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'bloodPressure':
|
|
|
+ className = 'BloodPressure';
|
|
|
+ const values = formData.value.split('/');
|
|
|
+ dataObject.systolic = parseInt(values[0]);
|
|
|
+ dataObject.diastolic = parseInt(values[1]);
|
|
|
+ dataObject.pulse = parseInt(formData.pulse) || 0;
|
|
|
+ if (formData.notes) dataObject.notes = formData.notes;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'heartRate':
|
|
|
+ className = 'HeartRate';
|
|
|
+ dataObject.heartRate = parseInt(formData.value);
|
|
|
+ dataObject.measurementMethod = formData.measurementMethod || '手动测量';
|
|
|
+ if (formData.notes) dataObject.notes = formData.notes;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ const healthData = new CloudObject(className);
|
|
|
+ healthData.set(dataObject);
|
|
|
+ await healthData.save();
|
|
|
+
|
|
|
+ const alert = await this.alertCtrl.create({
|
|
|
+ header: '成功',
|
|
|
+ message: '数据添加成功',
|
|
|
+ buttons: ['确定']
|
|
|
+ });
|
|
|
+ await alert.present();
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存数据失败:', error);
|
|
|
+
|
|
|
+ const alert = await this.alertCtrl.create({
|
|
|
+ header: '错误',
|
|
|
+ message: '数据添加失败,请重试',
|
|
|
+ buttons: ['确定']
|
|
|
+ });
|
|
|
+ await alert.present();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 辅助方法:获取血糖状态
|
|
|
+ getGlucoseStatus(value: number): string {
|
|
|
+ if (value <= 6.1) return '正常';
|
|
|
+ if (value <= 7.0) return '偏高';
|
|
|
+ return '过高';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 辅助方法:获取血糖颜色
|
|
|
+ getGlucoseColor(value: number): string {
|
|
|
+ if (value <= 6.1) return 'success';
|
|
|
+ if (value <= 7.0) return 'warning';
|
|
|
+ return 'danger';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 辅助方法:获取血压状态
|
|
|
+ getPressureStatus(systolic: number, diastolic: number): string {
|
|
|
+ if (systolic <= 120 && diastolic <= 80) return '正常';
|
|
|
+ if ((systolic <= 140 && diastolic <= 90)) return '偏高';
|
|
|
+ return '过高';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 辅助方法:获取血压颜色
|
|
|
+ getPressureColor(systolic: number, diastolic: number): string {
|
|
|
+ if (systolic <= 120 && diastolic <= 80) return 'success';
|
|
|
+ if ((systolic <= 140 && diastolic <= 90)) return 'warning';
|
|
|
+ return 'danger';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 辅助方法:获取心率状态
|
|
|
+ getHeartRateStatus(value: number): string {
|
|
|
+ if (value >= 60 && value <= 100) return '正常';
|
|
|
+ return '异常';
|
|
|
+ }
|
|
|
|
|
|
+ // 辅助方法:获取心率颜色
|
|
|
+ getHeartRateColor(value: number): string {
|
|
|
+ if (value >= 60 && value <= 100) return 'success';
|
|
|
+ return 'warning';
|
|
|
+ }
|
|
|
async loadHealthData() {
|
|
|
try {
|
|
|
this.healthData.loading = true;
|
|
@@ -57,12 +277,117 @@ async loadHealthData() {
|
|
|
heartRate: heartRate,
|
|
|
loading: false
|
|
|
};
|
|
|
+ this.updateMetricsOverview();
|
|
|
} catch (error) {
|
|
|
console.error('加载失败:', error);
|
|
|
this.healthData.loading = false;
|
|
|
}
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// 更新概览数据
|
|
|
+ updateMetricsOverview() {
|
|
|
+ // 更新血糖数据
|
|
|
+ if (this.healthData.bloodGlucose.length > 0) {
|
|
|
+ const latestGlucose = this.healthData.bloodGlucose[0];
|
|
|
+ const glucoseValue = latestGlucose.get('glucoseValue');
|
|
|
+ const measureTime = new Date(latestGlucose.get('measurementTime'));
|
|
|
+
|
|
|
+ this.metrics[0].value = glucoseValue.toFixed(1);
|
|
|
+ this.metrics[0].time = this.formatTime(measureTime);
|
|
|
+ this.metrics[0].timestamp = measureTime;
|
|
|
+
|
|
|
+ // 设置趋势和异常状态
|
|
|
+ if (glucoseValue > 6.1) {
|
|
|
+ this.metrics[0].abnormal = true;
|
|
|
+ this.metrics[0].trendColor = glucoseValue > 7.0 ? 'danger' : 'warning';
|
|
|
+ } else {
|
|
|
+ this.metrics[0].abnormal = false;
|
|
|
+ this.metrics[0].trendColor = 'success';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新血压数据
|
|
|
+ if (this.healthData.bloodPressure.length > 0) {
|
|
|
+ const latestPressure = this.healthData.bloodPressure[0];
|
|
|
+ const systolic = latestPressure.get('systolic');
|
|
|
+ const diastolic = latestPressure.get('diastolic');
|
|
|
+ const measureTime = new Date(latestPressure.get('measurementTime'));
|
|
|
+
|
|
|
+ this.metrics[1].value = `${systolic}/${diastolic}`;
|
|
|
+ this.metrics[1].time = this.formatTime(measureTime);
|
|
|
+ this.metrics[1].timestamp = measureTime;
|
|
|
+
|
|
|
+ // 设置趋势和异常状态
|
|
|
+ if (systolic > 120 || diastolic > 80) {
|
|
|
+ this.metrics[1].abnormal = true;
|
|
|
+ this.metrics[1].trendColor = (systolic > 140 || diastolic > 90) ? 'danger' : 'warning';
|
|
|
+ } else {
|
|
|
+ this.metrics[1].abnormal = false;
|
|
|
+ this.metrics[1].trendColor = 'success';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新心率数据
|
|
|
+ if (this.healthData.heartRate.length > 0) {
|
|
|
+ const latestHeartRate = this.healthData.heartRate[0];
|
|
|
+ const heartRateValue = latestHeartRate.get('heartRate');
|
|
|
+ const measureTime = new Date(latestHeartRate.get('measurementTime'));
|
|
|
+
|
|
|
+ this.metrics[2].value = heartRateValue.toString();
|
|
|
+ this.metrics[2].time = this.formatTime(measureTime);
|
|
|
+ this.metrics[2].timestamp = measureTime;
|
|
|
+
|
|
|
+ // 设置趋势和异常状态
|
|
|
+ if (heartRateValue < 60 || heartRateValue > 100) {
|
|
|
+ this.metrics[2].abnormal = true;
|
|
|
+ this.metrics[2].trendColor = 'warning';
|
|
|
+ } else {
|
|
|
+ this.metrics[2].abnormal = false;
|
|
|
+ this.metrics[2].trendColor = 'success';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 格式化时间显示
|
|
|
+ formatTime(date: any): string {
|
|
|
+ if (!date) return '暂无数据';
|
|
|
+
|
|
|
+ let dateObj: Date;
|
|
|
+
|
|
|
+ if (date instanceof Date) {
|
|
|
+ dateObj = date;
|
|
|
+ } else if (typeof date === 'string') {
|
|
|
+ dateObj = new Date(date);
|
|
|
+ } else if (typeof date === 'number') {
|
|
|
+ dateObj = new Date(date);
|
|
|
+ } else {
|
|
|
+ return '暂无数据';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isNaN(dateObj.getTime())) {
|
|
|
+ return '无效日期';
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.formatDate(dateObj);
|
|
|
}
|
|
|
|
|
|
+private formatDate(date: Date): string {
|
|
|
+ const now = new Date();
|
|
|
+ const diffHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60));
|
|
|
+
|
|
|
+ if (diffHours < 24) {
|
|
|
+ return `今天 ${this.formatTwoDigits(date.getHours())}:${this.formatTwoDigits(date.getMinutes())}`;
|
|
|
+ } else if (diffHours < 48) {
|
|
|
+ return `昨天 ${this.formatTwoDigits(date.getHours())}:${this.formatTwoDigits(date.getMinutes())}`;
|
|
|
+ } else {
|
|
|
+ return `${date.getMonth() + 1}月${date.getDate()}日 ${this.formatTwoDigits(date.getHours())}:${this.formatTwoDigits(date.getMinutes())}`;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 更新数据查询方法
|
|
|
private async queryData(className: string, user: CloudUser, date: Date): Promise<any[]> {
|
|
|
const query = new CloudQuery(className);
|
|
|
query.equalTo('patient', {
|
|
@@ -72,9 +397,24 @@ private async queryData(className: string, user: CloudUser, date: Date): Promise
|
|
|
});
|
|
|
query.greaterThan('measurementTime', date);
|
|
|
query.ascending('measurementTime');
|
|
|
- return await query.find();
|
|
|
+
|
|
|
+ const results = await query.find();
|
|
|
+
|
|
|
+ // 转换日期格式
|
|
|
+ return results.map(item => {
|
|
|
+ const time = item.get('measurementTime');
|
|
|
+ if (time && !(time instanceof Date)) {
|
|
|
+ item.data['measurementTime'] = new Date(time);
|
|
|
+ }
|
|
|
+ return item;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
+ // 辅助函数:格式化两位数
|
|
|
+ formatTwoDigits(num: number): string {
|
|
|
+ return num < 10 ? `0${num}` : num.toString();
|
|
|
+ }
|
|
|
+
|
|
|
async importData() {
|
|
|
try {
|
|
|
this.healthData = {
|
|
@@ -101,26 +441,7 @@ async importData() {
|
|
|
|
|
|
timeRange = 'week';
|
|
|
|
|
|
- metrics = [
|
|
|
- {
|
|
|
- name: '血糖',
|
|
|
- value: '6.5',
|
|
|
- unit: 'mmol/L',
|
|
|
- trend: 'caret-up',
|
|
|
- trendColor: 'success',
|
|
|
- reference: '3.9-6.1',
|
|
|
- abnormal: false
|
|
|
- },
|
|
|
- {
|
|
|
- name: '血压',
|
|
|
- value: '135/85',
|
|
|
- unit: 'mmHg',
|
|
|
- trend: 'caret-down',
|
|
|
- trendColor: 'danger',
|
|
|
- reference: '120/80',
|
|
|
- abnormal: true
|
|
|
- }
|
|
|
- ];
|
|
|
+
|
|
|
|
|
|
tasks = [
|
|
|
{
|