欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。


在健康管理领域,心理健康同样重要。今天我们来深入分析一个基于 React Native 开发的心理健康自测应用,该应用提供 PHQ-9 抑郁量表、GAD-7 焦虑量表等自测工具,帮助用户评估自己的心理健康状况,并支持将测试结果分享给心理医生,为心理健康管理提供科学依据。

使用 SafeAreaViewTouchableOpacityScrollViewModal 等实现跨平台兼容的用户界面

该应用采用了 React Native 现代开发技术栈,主要包括:

  • React Hooks:使用 useState 管理应用状态,包括量表数据、测试结果和用户输入等
  • TypeScript:通过类型定义确保数据结构的一致性和代码的可维护性
  • 跨平台组件:使用 SafeAreaViewTouchableOpacityScrollViewModal 等实现跨平台兼容的用户界面
  • 响应式布局:利用 Dimensions API 获取屏幕尺寸,确保在不同设备上的良好显示效果
  • 基础组件:使用 ViewTextTextInput 等构建用户界面

表示量表信息,包含 ID、名称、描述和问题列表

应用通过 TypeScript 定义了三个核心数据类型,构建了完整的心理健康自测数据模型:

  • Scale:表示量表信息,包含 ID、名称、描述和问题列表
  • Question:表示问题类型,包含 ID、问题文本和选项列表
  • TestResult:表示测试结果,包含 ID、量表 ID、用户 ID、得分和日期

这种强类型定义不仅提高了代码的可读性和可维护性,也为鸿蒙跨端适配提供了清晰的数据结构映射基础。

状态管理

应用采用了基于 useState 的轻量级状态管理方案,为不同功能模块分别管理状态:

  • 静态数据(量表列表)通过初始化的 useState 直接存储
  • 动态数据(测试结果)通过 useState 管理,并支持添加新数据
  • 交互状态(选中的量表、用户答案、模态框可见性)通过独立的 useState 管理

这种模式在中小型应用中非常高效,既避免了过度设计,又保证了状态管理的清晰性。


量表管理

量表管理是应用的基础功能,通过列表展示不同的心理健康量表,包括 PHQ-9 抑郁量表和 GAD-7 焦虑量表。用户可以点击选择量表进行测试,为心理健康评估提供标准化的工具。

自测功能是应用的核心功能,主要包括:

  • 展示量表的问题和选项
  • 允许用户选择答案
  • 计算测试得分
  • 保存测试结果
  • 显示测试完成通知

应用通过简单的得分计算逻辑,根据用户选择的选项计算总分,为用户提供初步的心理健康评估。

结果管理功能帮助用户跟踪和分享测试结果:

  • 存储历史测试结果
  • 查看测试结果详情
  • 分享测试结果给心理医生

通过结果管理,用户可以了解自己的心理健康变化趋势,心理医生也可以根据测试结果提供更有针对性的建议。

应用通过卡片式布局展示量表信息和测试结果,使用 TouchableOpacity 组件实现交互功能,点击卡片后可以查看详细信息或执行相关操作。此外,应用使用 Modal 组件展示详细信息,提升了用户体验。


组件映射

在鸿蒙 OS 适配过程中,需要注意以下组件映射:

  • SafeAreaView:在鸿蒙上需要使用 SafeArea 组件或自定义适配
  • TouchableOpacity:对应鸿蒙的 ButtonText 组件配合点击事件
  • ScrollView:对应鸿蒙的 ListContainerScroll 组件
  • Modal:对应鸿蒙的 Dialog 组件
  • Alert:对应鸿蒙的 ToastDialogAlertDialog 组件
  • TextInput:对应鸿蒙的 TextField 组件

鸿蒙 OS 对 Flexbox 布局的支持与 React Native 基本一致,但在细节上仍需注意:

  • 确保样式命名符合鸿蒙规范
  • 调整间距和尺寸以适应鸿蒙设备的显示特性
  • 注意字体大小和行高的适配
  • 对于表单输入控件,需要适配鸿蒙的输入框样式和交互方式

在鸿蒙跨端开发中,性能优化是一个重要考虑因素:

  • 使用 memo 优化组件渲染,特别是对于量表列表、问题列表等重复渲染的场景
  • 合理使用 useCallbackuseMemo 减少不必要的计算
  • 优化图片资源,考虑使用鸿蒙的资源加载机制
  • 对于测试结果的计算,考虑使用更高效的算法
  • 对于列表数据的渲染,考虑使用虚拟化技术减少内存占用

标准化心理评估工具

应用集成了标准化的心理评估工具:

  • PHQ-9 抑郁量表:用于评估抑郁症状的严重程度
  • GAD-7 焦虑量表:用于评估焦虑症状的严重程度

这些标准化工具比非标准化的自测更具科学性和参考价值,能够为用户和心理医生提供更准确的评估结果。

应用实现了完整的测试流程:

  • 选择量表
  • 回答问题
  • 计算得分
  • 保存结果
  • 查看和分享结果

这种完整的测试流程为用户提供了从测试到结果管理的全方位心理健康自测服务。

应用实现了结果分享机制,允许用户将测试结果分享给心理医生。这种设计促进了用户与专业人士的沟通,为用户提供了获得专业帮助的途径。

应用采用了清晰的模块化设计:

  • 功能按模块划分(量表管理、自测功能、结果管理)
  • 组件职责单一,便于维护和扩展
  • 状态管理逻辑与 UI 渲染分离

Base64 图标库

应用使用 Base64 编码的图标,这种方式有几个优点:

  • 减少网络请求,提高加载速度
  • 避免图标资源的跨平台适配问题
  • 减小应用包体积

虽然示例中使用的是占位 Base64 编码,但实际应用中可以使用真实的图标编码。


文件结构

示例代码集中在 App.tsx 文件中,适合小型应用。对于大型应用,建议按功能模块拆分文件:

  • /components:存放可复用组件,如量表卡片、问题卡片、结果卡片等

  • /types:存放类型定义,如 Scale、Question、TestResult 等

  • /hooks:存放自定义 hooks,如测试逻辑、结果计算逻辑等

  • /services:存放 API 调用和业务逻辑,如数据存储、结果分析服务等

  • /utils:存放工具函数,如日期处理、数据格式化等

  • 命名规范:变量和函数命名清晰,符合语义化要求

  • 类型安全:使用 TypeScript 确保类型安全

  • 错误处理:通过条件判断处理可能的异常情况

  • 注释:代码结构清晰,关键部分有适当注释

  • 性能考虑:合理使用 React Hooks,避免不必要的渲染和计算

应用架构具有良好的扩展性:

  • 可以轻松添加新的心理量表
  • 可以集成更复杂的得分计算和分析逻辑
  • 可以扩展支持更多的心理健康评估工具
  • 可以集成云服务,实现测试结果的云端存储和多设备同步
  • 可以添加社区功能,让用户分享心理健康经验和资源

这个心理健康自测应用展示了如何使用 React Native 构建功能完备的心理健康评估工具,特别是在标准化量表集成和结果管理方面的实践具有重要参考价值。通过跨端开发技术,可以在不同平台为用户提供一致的服务体验。

随着人们对心理健康重视程度的提高,这类心理健康自测应用的需求将不断增长。未来可以考虑:

  • 集成更多标准化的心理评估工具,覆盖更广泛的心理健康问题
  • 利用 AI 技术根据测试结果提供初步的心理健康建议
  • 支持远程心理咨询,让用户直接与心理医生进行视频或文字咨询
  • 开发配套的心理医生端应用,实现远程心理健康管理
  • 与医疗机构对接,实现测试结果的医疗级别的专业分析

React Native 鸿蒙跨端开发代表了移动应用开发的未来趋势,通过一套代码库覆盖多个平台,不仅可以降低开发成本,还可以确保用户体验的一致性。在心理健康应用领域,这种开发模式尤为重要,因为它可以让开发者更专注于核心功能的实现,而不是平台差异的处理。


心理健康自测类应用作为医疗健康领域的重要细分场景,需要兼顾量表专业性、交互易用性与多端覆盖能力。React Native 凭借其跨端开发特性,成为这类应用快速落地多平台的核心技术选型;而鸿蒙(HarmonyOS)的分布式能力与安全特性,又为心理健康数据的跨设备流转、隐私保护提供了全新的技术支撑。本文以 PHQ-9/GAD-7 量表自测应用为例,深度拆解 React Native 应用的核心技术架构,并系统阐述其向鸿蒙生态跨端适配的关键技术路径与最佳实践。

本次案例中的心理健康自测应用,聚焦于标准化心理量表的自测流程、结果计算与分享,其技术架构充分体现了 React Native 函数式组件开发的轻量化特性,同时兼顾了心理量表的专业性与数据的安全性:

1. 心理量表的结构化定义

应用基于 TypeScript 构建了完整的心理量表数据模型体系,从量表定义、问题选项到测试结果,形成了闭环的数据链路,这是保证心理自测结果准确性与跨端复用性的核心基础:

// 量表模型:标准化心理测评工具的核心定义
type Scale = {
  id: string;
  name: string; // 量表名称(如PHQ-9抑郁量表)
  description: string; // 量表用途,保证专业性
  questions: Question[]; // 嵌套问题列表,体现1:N的业务关联
};

// 问题模型:量表核心测评单元
type Question = {
  id: string;
  text: string; // 测评问题文本,严格遵循标准化量表表述
  options: string[]; // 选项列表,对应不同严重程度的评分维度
};

// 测试结果模型:自测数据的量化存储
type TestResult = {
  id: string;
  scaleId: string; // 关联量表ID,实现结果与量表的绑定
  userId: string; // 关联用户ID,保证数据归属
  score: number; // 量化得分,核心测评结果
  date: string; // 测试日期,用于追踪心理健康变化
};

这套数据模型严格遵循“量表-问题-结果”的业务逻辑,所有字段均采用基础数据类型,无平台相关依赖,为跨端数据流转与解析奠定了纯净的结构化基础。同时强类型定义避免了因数据类型错误导致的量表评分计算异常,符合心理健康测评的专业要求。

2. 状态管理

应用采用 React 核心的 useState Hook 实现轻量化状态管理,同时封装了心理量表的核心评分逻辑,体现了心理健康自测应用“交互精准、计算准确”的核心特性:

  • 基础数据状态scales(量表列表)作为静态专业数据,采用 useState 初始化后不再变更;testResults(测试结果)采用不可变数据模式更新,保证自测记录的可追溯性;
  • 交互状态selectedScale 标记当前选中的自测量表,answers 以键值对形式存储用户答题结果({questionId: answer}),实现多问题答题状态的精准管理;isModalVisible/modalContent 封装通用弹窗逻辑,统一处理结果详情展示;
  • 核心评分逻辑:通过 reduce 方法实现量表得分的自动化计算,严格遵循标准化量表的评分规则:
    const handleSubmitTest = () => {
      if (selectedScale) {
        const scale = scales.find(s => s.id === selectedScale);
        if (scale) {
          // 核心评分逻辑:选项索引作为得分,累加所有问题得分
          const score = Object.values(answers).reduce((total, answer) => {
            const optionIndex = scale.questions[0].options.indexOf(answer);
            return total + optionIndex;
          }, 0);
    
          const newResult: TestResult = {
            id: (testResults.length + 1).toString(),
            scaleId: selectedScale,
            userId: '1',
            score,
            date: new Date().toISOString().split('T')[0]
          };
    
          setTestResults([...testResults, newResult]); // 不可变更新结果列表
          setSelectedScale(null);
          setAnswers({}); // 清空答题状态
          Alert.alert('测试完成', `您的${scale.name}得分为 ${score}`);
        }
      }
    };
    
    这里的关键技术点在于选项索引映射评分:将用户选择的选项(如“几天”)转换为对应的分值(索引值),累加得到总分,完全符合 PHQ-9/GAD-7 等标准化量表的评分规则。这段计算逻辑纯基于 ES6+ 标准语法,无任何 React Native 平台特定 API,可直接复用于鸿蒙端。

3. 交互设计

应用基于 React Native 基础组件构建了符合心理健康自测场景的交互体系,兼顾了专业性与易用性:

  • 量表选择交互:通过 selectedCard 样式实现选中量表的高亮反馈,直观提示用户当前自测对象;
  • 答题交互:采用 TouchableOpacity 实现选项按钮,selectedOption 样式标记已选答案,避免用户重复选择或漏选,符合心理自测“单题单选”的交互规则;
  • 结果展示交互:卡片式布局展示自测结果,核心指标(得分、日期)突出显示,同时提供“查看详情”与“分享”按钮,满足用户查看与分享自测结果的核心需求;
  • 弹窗交互:通过 Modal 组件实现结果详情弹窗,slide 动画降低用户视觉冲击,符合心理健康应用“温和、舒适”的交互设计原则。

4. 样式

应用通过 StyleSheet.create 封装了统一的样式规范,完全基于 React Native 跨平台通用属性,为鸿蒙适配降低了样式迁移成本:

  • 布局体系:采用 Flex 布局实现卡片的自适应排列,SafeAreaView 保障不同设备的安全区域适配,ScrollView 承载长列表内容(量表列表、结果列表);
  • 视觉层级:通过 elevation/shadow 属性增强卡片的视觉层级,borderRadius 提升界面的柔和度,符合心理健康应用的视觉设计要求;
  • 交互反馈样式selectedCard/selectedOption 通过边框/背景色变化实现选中状态反馈,颜色选用低饱和度的蓝色系,避免视觉刺激,符合心理健康场景的设计原则。

鸿蒙系统的分布式能力与隐私保护特性,与心理健康自测应用的跨设备使用、数据安全需求高度契合。将本应用适配到鸿蒙生态,核心在于“复用业务逻辑,适配底层组件,强化分布式安全”:

1. 适配方案

针对本应用的轻量级特性与心理数据的专业性要求,优先选择“React Native 业务逻辑复用 + 鸿蒙 ArkUI 组件替换”的适配方案,核心原则是:

  • 100% 复用纯业务逻辑:量表评分计算、答题状态管理、结果存储等纯 JS/TS 逻辑代码无需修改,直接迁移到鸿蒙工程;
  • 组件层精准映射:将 React Native 组件替换为鸿蒙 ArkUI 组件,保持布局结构与交互逻辑一致,核心组件映射关系如下:
React Native 组件 鸿蒙 ArkUI 组件 适配核心要点
SafeAreaView SafeArea + Column 鸿蒙通过 SafeArea 组件实现安全区域适配,外层嵌套 Column 保持布局结构
View Stack/Column/Row 根据布局方向选择鸿蒙容器组件,语义更明确
Text Text 属性基本兼容,仅调整样式属性命名(如 fontWeight: 'bold'fontWeight: FontWeight.Bold
TouchableOpacity Button/Text(绑定 onClick 鸿蒙无 TouchableOpacity 组件,通过 ButtonText 绑定点击事件实现交互,选项按钮通过 Button 组件适配
ScrollView Scroll 滚动容器属性完全兼容,仅调整样式写法
Modal Dialog 鸿蒙通过 Dialog 组件实现弹窗,适配显隐控制逻辑
TextInput Input 本应用无输入框,但如需扩展,onChangeText 需适配为 onChange 事件回调

以量表答题选项为例,鸿蒙适配后的核心代码示例:

// 鸿蒙 TS/JS 适配代码
@Entry
@Component
struct ScaleQuestionCard {
  @State selectedScale: string = '1';
  @State scales: Scale[] = [...]; // 复用原量表数据
  @State answers: { [key: string]: string } = {}; // 复用答题状态

  build() {
    Column() {
      if (this.selectedScale) {
        const currentScale = this.scales.find(s => s.id === this.selectedScale);
        if (currentScale) {
          ForEach(currentScale.questions, (question) => {
            Column() {
              Text(question.text)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
                .marginBottom(8);
              ForEach(question.options, (option) => {
                Button(option)
                  .backgroundColor(this.answers[question.id] === option ? '#0284c7' : '#f0f9ff')
                  .fontColor(this.answers[question.id] === option ? '#ffffff' : '#0c4a6e')
                  .borderRadius(8)
                  .padding(12)
                  .marginBottom(8)
                  .onClick(() => this.handleAnswerChange(question.id, option));
              })
            }.marginBottom(16);
          })
          // 提交按钮
          Button('提交测试')
            .backgroundColor('#0284c7')
            .borderRadius(12)
            .padding(16)
            .onClick(() => this.handleSubmitTest());
        }
      }
    }
  }

  // 完全复用原 handleAnswerChange 逻辑
  handleAnswerChange(questionId: string, answer: string) {
    this.answers = {
      ...this.answers,
      [questionId]: answer
    };
  }

  // 完全复用原 handleSubmitTest 评分逻辑
  handleSubmitTest() {
    // 复用 React Native 中的评分与结果存储逻辑
    const currentScale = this.scales.find(s => s.id === this.selectedScale);
    if (currentScale) {
      const score = Object.values(this.answers).reduce((total, answer) => {
        const optionIndex = currentScale.questions[0].options.indexOf(answer);
        return total + optionIndex;
      }, 0);
      // 鸿蒙端结果存储逻辑
      // ...
    }
  }
}

2. 核心差异

React Native 基于 React Hooks 实现状态管理,而鸿蒙 JS/TS 框架采用装饰器模式的状态管理,核心适配要点如下:

  • 状态定义适配:React 的 useState → 鸿蒙的 @State/@Link 装饰器,例如:
    // React Native 状态定义
    const [selectedScale, setSelectedScale] = useState<string | null>(null);
    const [answers, setAnswers] = useState<{ [key: string]: string }>({});
    
    // 鸿蒙适配后
    @State selectedScale: string | null = null;
    @State answers: { [key: string]: string } = {};
    
  • 状态更新适配:React Native 中通过 setAnswers(prev => ({...prev, [key]: value})) 的不可变更新方式,在鸿蒙中可直接通过对象扩展实现,因为鸿蒙的 @State 装饰器会监听对象的变更:
    // 鸿蒙中更新答题状态
    handleAnswerChange(questionId: string, answer: string) {
      this.answers = {
        ...this.answers,
        [questionId]: answer
      };
    }
    
  • 弹窗逻辑适配:React Native 的 Modal 组件逻辑,在鸿蒙中适配为 Dialog 组件,通过 @State 控制显隐:
    // 鸿蒙弹窗控制
    @State isModalVisible: boolean = false;
    @State modalContent: string = '';
    
    build() {
      Column() {
        // 其他组件...
        Dialog.show({
          builder: this.dialogBuilder(),
          show: this.isModalVisible,
          onClose: () => {
            this.isModalVisible = false;
          }
        });
      }
    }
    
    dialogBuilder() {
      return Column() {
        Text('详细信息')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .marginBottom(12);
        Text(this.modalContent)
          .fontSize(14)
          .marginBottom(20);
        Button('关闭')
          .backgroundColor('#0284c7')
          .borderRadius(8)
          .padding(10)
          .onClick(() => {
            this.isModalVisible = false;
          });
      }
      .width('80%')
      .backgroundColor('#ffffff')
      .borderRadius(12)
      .padding(20);
    }
    

适配鸿蒙的核心价值不仅是“多端运行”,更是利用其分布式能力与安全特性提升心理健康自测应用的核心体验与数据安全性:

  • 分布式数据同步:将 testResults 自测结果存储到鸿蒙分布式数据中心,并开启数据加密,实现手机端自测、平板端查看历史记录、电脑端分享给心理医生的跨设备数据同步,同时保证心理数据的隐私安全:
    // 鸿蒙分布式加密存储
    import distributedData from '@ohos.data.distributedData';
    import crypto from '@ohos.crypto';
    
    // 初始化加密的分布式数据存储
    private kvStore: distributedData.KVStore = null;
    
    async onInit() {
      const kvManager = distributedData.createKVManager({
        context: getContext(this),
        bundleName: 'com.example.mentalhealth'
      });
      // 创建加密的KV存储
      this.kvStore = await kvManager.getKVStore('mental_health_store', {
        encrypt: true, // 开启数据加密
        securityLevel: distributedData.SecurityLevel.S1 // 安全级别
      });
    }
    
    // 存储自测结果到分布式存储
    saveTestResult(result: TestResult) {
      this.kvStore.put(`result_${result.id}`, JSON.stringify(result));
    }
    
  • 分布式分享能力:利用鸿蒙的分布式流转能力,实现自测结果一键分享给心理医生的鸿蒙设备,同时通过鸿蒙的权限管理,保证分享过程中的数据安全;
  • 隐私保护扩展:基于鸿蒙的应用隐私保护能力,对自测结果进行脱敏处理(如隐藏用户ID),仅保留量表得分与日期,符合心理健康数据的隐私保护规范。

4. 样式

React Native 的 Flex 布局与鸿蒙的 Flex 布局语法高度兼容,仅需少量样式属性适配即可保证视觉体验的一致性,核心适配点:

  • 样式属性命名适配:React Native 的 flexDirection: 'row' → 鸿蒙的 flexDirection: FlexDirection.RowalignItems: 'center'alignItems: ItemAlign.Center
  • 交互样式适配:React Native 的 selectedOption 背景色逻辑,在鸿蒙中通过三元表达式直接绑定到 Button 组件的 backgroundColor 属性;
  • 阴影效果适配:React Native 的 elevation/shadow 属性,在鸿蒙中通过 shadow 组件实现,保持视觉层级一致:
    // React Native 阴影样式
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    
    // 鸿蒙适配后
    .shadow({
      color: '#000',
      offsetX: 0,
      offsetY: 2,
      opacity: 0.1,
      radius: 4
    })
    

针对心理健康自测这类医疗健康类跨端应用,核心最佳实践需兼顾专业性、数据安全性与多端体验一致性:

  1. 业务逻辑与UI解耦:将量表评分、结果计算等核心逻辑封装为纯函数,与 UI 组件解耦,保证跨端复用性,同时便于后续接入专业的心理量表计算引擎;
  2. 数据模型标准化:采用 TypeScript 强类型定义所有心理量表数据,确保跨端数据解析的一致性,避免因数据类型错误导致的评分异常;
  3. 列表性能优化:在鸿蒙端将 map 渲染的长列表(如自测结果列表)替换为鸿蒙的 LazyForEach 组件,实现列表项的懒加载,降低内存占用;
  4. 隐私安全强化:在鸿蒙端利用系统级加密能力,对自测结果进行加密存储与传输,符合医疗健康数据的隐私保护要求;
  5. 交互体验适配:针对鸿蒙不同设备(手机、平板、智慧屏)的交互特性,调整按钮大小、字体尺寸等,保证不同设备的操作便捷性。

本文以心理健康自测应用为例,系统解析了 React Native 应用的核心技术架构,包括强类型心理量表数据模型、Hooks 驱动的评分逻辑、场景化交互设计等,并深入探讨了向鸿蒙系统跨端适配的完整技术路径。React Native 应用的“纯 JS 业务逻辑 + 标准化数据模型”为鸿蒙适配提供了低成本复用基础,而鸿蒙的分布式能力与安全特性则为心理健康自测应用赋予了跨设备数据同步、隐私保护的核心优势。对于医疗健康类跨端应用开发而言,这种“React Native 跨端开发 + 鸿蒙分布式安全扩展”的模式,既保证了多端开发效率,又能充分利用鸿蒙生态的技术优势,是心理健康类应用跨端落地的最优路径之一。


真实演示案例代码:






// App.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput, Modal } from 'react-native';

// Base64 图标库
const ICONS_BASE64 = {
  phq9: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  gad7: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  result: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  share: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};

const { width, height } = Dimensions.get('window');

// 量表类型
type Scale = {
  id: string;
  name: string;
  description: string;
  questions: Question[];
};

// 问题类型
type Question = {
  id: string;
  text: string;
  options: string[];
};

// 测试结果类型
type TestResult = {
  id: string;
  scaleId: string;
  userId: string;
  score: number;
  date: string;
};

// 心理健康自测应用组件
const MentalHealthSelfTestApp: React.FC = () => {
  const [scales] = useState<Scale[]>([
    {
      id: '1',
      name: 'PHQ-9抑郁量表',
      description: '用于评估抑郁症状的严重程度',
      questions: [
        {
          id: '1',
          text: '过去两周内,您是否感到情绪低落、沮丧或绝望?',
          options: ['完全没有', '几天', '一半以上的日子', '几乎每天']
        },
        {
          id: '2',
          text: '过去两周内,您是否对做事失去兴趣或乐趣?',
          options: ['完全没有', '几天', '一半以上的日子', '几乎每天']
        }
      ]
    },
    {
      id: '2',
      name: 'GAD-7焦虑量表',
      description: '用于评估焦虑症状的严重程度',
      questions: [
        {
          id: '1',
          text: '过去两周内,您是否感到紧张、焦虑或不安?',
          options: ['完全没有', '几天', '一半以上的日子', '几乎每天']
        },
        {
          id: '2',
          text: '过去两周内,您是否难以控制担忧?',
          options: ['完全没有', '几天', '一半以上的日子', '几乎每天']
        }
      ]
    }
  ]);

  const [testResults, setTestResults] = useState<TestResult[]>([
    {
      id: '1',
      scaleId: '1',
      userId: '1',
      score: 15,
      date: '2023-12-01'
    },
    {
      id: '2',
      scaleId: '2',
      userId: '1',
      score: 10,
      date: '2023-12-02'
    }
  ]);

  const [selectedScale, setSelectedScale] = useState<string | null>(null);
  const [answers, setAnswers] = useState<{ [key: string]: string }>({});
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [modalContent, setModalContent] = useState('');

  const handleSelectScale = (scaleId: string) => {
    setSelectedScale(scaleId);
    setAnswers({});
    Alert.alert('选择量表', '您已选择该量表进行测试');
  };

  const handleAnswerChange = (questionId: string, answer: string) => {
    setAnswers(prevAnswers => ({
      ...prevAnswers,
      [questionId]: answer
    }));
  };

  const handleSubmitTest = () => {
    if (selectedScale) {
      const scale = scales.find(s => s.id === selectedScale);
      if (scale) {
        const score = Object.values(answers).reduce((total, answer) => {
          const optionIndex = scale.questions[0].options.indexOf(answer);
          return total + optionIndex;
        }, 0);

        const newResult: TestResult = {
          id: (testResults.length + 1).toString(),
          scaleId: selectedScale,
          userId: '1',
          score,
          date: new Date().toISOString().split('T')[0]
        };

        setTestResults([...testResults, newResult]);
        setSelectedScale(null);
        setAnswers({});
        Alert.alert('测试完成', `您的${scale.name}得分为 ${score}`);
      }
    }
  };

  const handleViewResult = (resultId: string) => {
    const result = testResults.find(r => r.id === resultId);
    if (result) {
      const scale = scales.find(s => s.id === result.scaleId);
      setModalContent(`量表: ${scale?.name}\n得分: ${result.score}\n日期: ${result.date}`);
      setIsModalVisible(true);
    }
  };

  const handleShareResult = (resultId: string) => {
    const result = testResults.find(r => r.id === resultId);
    if (result) {
      Alert.alert('分享结果', `已将${result.scaleId}的结果分享给心理医生`);
    }
  };

  const openModal = (content: string) => {
    setModalContent(content);
    setIsModalVisible(true);
  };

  const closeModal = () => {
    setIsModalVisible(false);
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>心理健康自测</Text>
        <Text style={styles.subtitle}>提供PHQ-9抑郁量表、GAD-7焦虑量表等自测工具,结果可分享至心理医生</Text>
      </View>

      <ScrollView style={styles.content}>
        {/* 量表列表 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>量表列表</Text>
          {scales.map(scale => (
            <TouchableOpacity 
              key={scale.id}
              style={[
                styles.card,
                selectedScale === scale.id && styles.selectedCard
              ]}
              onPress={() => handleSelectScale(scale.id)}
            >
              <Text style={styles.icon}>📋</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>{scale.name}</Text>
                <Text style={styles.cardDescription}>{scale.description}</Text>
              </View>
            </TouchableOpacity>
          ))}
        </View>

        {/* 量表测试 */}
        {selectedScale && (
          <View style={styles.section}>
            <Text style={styles.sectionTitle}>量表测试</Text>
            {scales.find(s => s.id === selectedScale)?.questions.map(question => (
              <View key={question.id} style={styles.questionContainer}>
                <Text style={styles.questionText}>{question.text}</Text>
                {question.options.map(option => (
                  <TouchableOpacity 
                    key={option}
                    style={[
                      styles.optionButton,
                      answers[question.id] === option && styles.selectedOption
                    ]}
                    onPress={() => handleAnswerChange(question.id, option)}
                  >
                    <Text style={styles.optionText}>{option}</Text>
                  </TouchableOpacity>
                ))}
              </View>
            ))}
            <TouchableOpacity 
              style={styles.submitButton}
              onPress={handleSubmitTest}
            >
              <Text style={styles.submitText}>提交测试</Text>
            </TouchableOpacity>
          </View>
        )}

        {/* 测试结果 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>测试结果</Text>
          {testResults.map(result => (
            <View key={result.id} style={styles.resultCard}>
              <Text style={styles.icon}>📊</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>结果ID: {result.id}</Text>
                <Text style={styles.cardDescription}>得分: {result.score}</Text>
                <Text style={styles.cardDescription}>日期: {result.date}</Text>
              </View>
              <TouchableOpacity 
                style={styles.viewButton}
                onPress={() => handleViewResult(result.id)}
              >
                <Text style={styles.viewText}>查看详情</Text>
              </TouchableOpacity>
              <TouchableOpacity 
                style={styles.shareButton}
                onPress={() => handleShareResult(result.id)}
              >
                <Text style={styles.shareText}>分享</Text>
              </TouchableOpacity>
            </View>
          ))}
        </View>

        {/* 使用说明 */}
        <View style={styles.infoCard}>
          <Text style={styles.sectionTitle}>📘 使用说明</Text>
          <Text style={styles.infoText}>• 选择合适的心理量表进行自测</Text>
          <Text style={styles.infoText}>• 根据自身情况选择最符合的选项</Text>
          <Text style={styles.infoText}>• 提交测试后查看结果</Text>
          <Text style={styles.infoText}>• 可将结果分享给心理医生</Text>
        </View>

        {/* 弹框内容 */}
        <Modal
          animationType="slide"
          transparent={true}
          visible={isModalVisible}
          onRequestClose={closeModal}
        >
          <View style={styles.modalContainer}>
            <View style={styles.modalContent}>
              <Text style={styles.modalTitle}>详细信息</Text>
              <Text style={styles.modalText}>{modalContent}</Text>
              <TouchableOpacity
                style={styles.closeButton}
                onPress={closeModal}
              >
                <Text style={styles.closeButtonText}>关闭</Text>
              </TouchableOpacity>
            </View>
          </View>
        </Modal>
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f9ff',
  },
  header: {
    flexDirection: 'column',
    padding: 16,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#bae6fd',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#0c4a6e',
    marginBottom: 4,
  },
  subtitle: {
    fontSize: 14,
    color: '#0284c7',
  },
  content: {
    flex: 1,
    marginTop: 12,
  },
  section: {
    backgroundColor: '#ffffff',
    marginHorizontal: 16,
    marginBottom: 12,
    borderRadius: 12,
    padding: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  sectionTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#0c4a6e',
    marginBottom: 12,
  },
  card: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#f0f9ff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 12,
  },
  selectedCard: {
    borderWidth: 2,
    borderColor: '#0284c7',
  },
  resultCard: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#f0f9ff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 12,
  },
  icon: {
    fontSize: 28,
    marginRight: 12,
  },
  cardInfo: {
    flex: 1,
  },
  cardTitle: {
    fontSize: 16,
    fontWeight: '500',
    color: '#0c4a6e',
    marginBottom: 4,
  },
  cardDescription: {
    fontSize: 14,
    color: '#0284c7',
    marginBottom: 2,
  },
  viewButton: {
    backgroundColor: '#0284c7',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
    marginRight: 8,
  },
  viewText: {
    color: '#ffffff',
    fontSize: 12,
    fontWeight: '500',
  },
  shareButton: {
    backgroundColor: '#10b981',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
  },
  shareText: {
    color: '#ffffff',
    fontSize: 12,
    fontWeight: '500',
  },
  questionContainer: {
    marginBottom: 16,
  },
  questionText: {
    fontSize: 16,
    fontWeight: '500',
    color: '#0c4a6e',
    marginBottom: 8,
  },
  optionButton: {
    backgroundColor: '#f0f9ff',
    padding: 12,
    borderRadius: 8,
    marginBottom: 8,
  },
  selectedOption: {
    backgroundColor: '#0284c7',
  },
  optionText: {
    fontSize: 14,
    color: '#0c4a6e',
    textAlign: 'center',
  },
  submitButton: {
    backgroundColor: '#0284c7',
    padding: 16,
    borderRadius: 12,
    alignItems: 'center',
  },
  submitText: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: '500',
  },
  infoCard: {
    backgroundColor: '#ffffff',
    marginHorizontal: 16,
    marginBottom: 80,
    borderRadius: 12,
    padding: 16,
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  infoText: {
    fontSize: 14,
    color: '#64748b',
    lineHeight: 20,
    marginBottom: 4,
  },
  modalContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
  },
  modalContent: {
    width: '80%',
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 20,
    elevation: 5,
  },
  modalTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#0c4a6e',
    marginBottom: 12,
    textAlign: 'center',
  },
  modalText: {
    fontSize: 14,
    color: '#0c4a6e',
    lineHeight: 20,
    marginBottom: 20,
  },
  closeButton: {
    backgroundColor: '#0284c7',
    padding: 10,
    borderRadius: 8,
    alignItems: 'center',
  },
  closeButtonText: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '500',
  },
});

export default MentalHealthSelfTestApp;



请添加图片描述


打包

接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

在这里插入图片描述

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

在这里插入图片描述

最后运行效果图如下显示:

请添加图片描述
摘要:本文介绍了一个基于React Native开发的心理健康自测应用,该应用集成了PHQ-9抑郁量表和GAD-7焦虑量表等标准化评估工具。应用采用TypeScript构建数据模型,使用React Hooks管理状态,并实现了量表选择、测试答题、结果计算与分享等完整功能流程。重点分析了该应用向鸿蒙生态跨端适配的关键技术路径,包括组件映射、布局适配和性能优化策略。通过React Native的跨平台特性,该应用可在多设备上提供一致的心理健康评估服务,同时满足专业量表测评的准确性和数据安全性要求。(149字)

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐