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


在企业考勤管理体系中,补卡申请是考勤闭环的关键环节,其核心诉求在于流程标准化、状态可追踪、审批结果实时同步,同时需适配多终端操作场景。鸿蒙系统的分布式全场景能力为企业级审批类应用提供了跨设备协同的底层支撑,而React Native凭借“一次开发、多端运行”的技术特性,成为补卡申请这类轻量级审批应用跨端开发的最优选择。本文将从状态枚举设计、审批流程闭环、跨端交互适配到鸿蒙生态融合的底层实现,全方位拆解这款补卡申请应用的技术架构,剖析React Native与鸿蒙生态深度融合的关键技术要点。

审批完成等关键节点推送提示

这款补卡申请应用的核心架构完全基于React Native通用API体系构建,未引入任何平台专属代码,这是实现鸿蒙跨端兼容的核心前提。React Native for HarmonyOS框架会将React Native的通用组件无缝映射为鸿蒙ArkUI原生组件,保证交互逻辑和视觉体验在鸿蒙设备上的原生质感。

从组件适配层面来看,TouchableOpacity作为核心交互组件,在鸿蒙系统中会被映射为Button原生组件,保留点击反馈的同时适配鸿蒙系统的交互规范——尤其适合员工选择、提交申请、审批操作这类需要明确操作反馈的场景,避免因跨端交互差异导致的操作误判;TextInput组件转换为鸿蒙TextInput原生控件,支持补卡日期、补卡原因等结构化/非结构化文本的精准录入,其placeholder属性在鸿蒙系统中会适配原生提示文本样式,保证输入引导的一致性;Modal组件则对应鸿蒙Dialog原生模态框,通过animationType="slide"实现的滑动弹窗效果,在鸿蒙系统中会解析为原生的滑动动效,用于展示补卡申请的完整详情,符合鸿蒙系统的交互体验标准;Alert组件调用鸿蒙系统级弹窗能力,在提交申请、审批完成等关键节点推送提示,确保审批状态的即时反馈,契合企业考勤审批“流程可感知、结果可确认”的核心诉求。

此外,应用通过Dimensions.get('window')获取设备屏幕尺寸,该API在鸿蒙系统中被适配为getWindowSize原生能力,能够精准识别鸿蒙手机、平板、智慧屏等不同形态设备的屏幕参数——例如在鸿蒙平板上展示补卡申请审批列表(适配大屏多数据展示场景),在手机上呈现精简的补卡申请提交界面,在智慧屏上适配企业考勤大屏的审批状态展示,完美契合企业多终端办公的业务场景。

审批状态的枚举型设计

补卡申请的核心是“员工-申请-审批状态”的三层关联,代码中通过TypeScript构建了强类型数据模型,尤其是审批状态的枚举型设计,既规避前端开发中的状态值错误,又在跨端编译阶段拦截数据格式偏差,适配鸿蒙ArkTS的静态类型特性。

// 员工档案模型:基础身份信息,支撑补卡申请的人员关联
type Employee = {
  id: string;
  name: string;
  department: string;         // 字符串型部门,适配企业组织架构的标准化描述
  position: string;            // 字符串型职位,兼容不同岗位的差异化表述
};

// 补卡申请核心模型:枚举型状态设计,实现审批流程的标准化
type 补卡申请 = {
  id: string;
  employeeId: string;          // 关联员工ID,实现申请与员工的精准绑定
  date: string;                // 字符串型日期,兼容跨端统一的YYYY-MM-DD格式
  reason: string;              // 字符串型原因,支持自定义补卡理由的完整录入
  status: 'pending' | 'approved' | 'rejected'; // 联合枚举类型,限定审批状态的合法值
};

其中,status字段采用'pending' | 'approved' | 'rejected'的联合枚举类型是核心设计亮点:在鸿蒙系统中,React Native的TypeScript编译器会对JS层与ArkTS层之间的状态传递进行严格校验,避免出现“审核中”“通过”等非标准化字符串导致的跨端状态解析错误;同时,枚举型状态设计也适配了企业审批流程的标准化诉求,pending(待审批)、approved(已批准)、rejected(已拒绝)的状态值在跨端传递时保持语义一致,不会因鸿蒙系统的语言环境或字符编码差异导致状态判定错误。employeeId作为关联字段,保证了补卡申请与员工档案的精准关联,在跨端数据交互中避免因ID格式错误导致的人员信息查询失败,符合企业考勤“一人一申请”的核心管理要求。

React Hooks驱动的跨端审批流程

应用的核心业务逻辑(自动审批、手动提交申请、审批操作)均基于React Hooks(useStateuseEffect)实现,这种轻量级状态管理方式完美适配React Native的跨端生命周期模型,同时与鸿蒙组件生命周期深度融合,保障了补卡审批流程的跨端稳定运行。

不直接修改原数据

应用通过useState管理核心状态,包括员工列表、补卡申请列表、选中员工、新申请表单、弹窗状态等,所有状态更新均采用“不可变更新”策略——即不直接修改原数据,而是创建新的数据副本,这一设计在鸿蒙系统中尤为关键。

鸿蒙分布式数据管理要求数据副本的一致性,不可变更新保证每次状态变更都会生成新的数据源,避免多端数据同步时的冲突问题。例如提交新补卡申请时:

set补卡申请s([...补卡申请s, newApplication]);

通过解构赋值创建新数组,保留原有申请记录的同时添加新记录,确保鸿蒙多设备同步时数据的完整性;审批操作中同样遵循这一原则:

const updatedApplications =补卡申请s.map(app => 
  app.id === applicationId ? { ...app, status: 'approved' } : app
);
set补卡申请s(updatedApplications);

仅修改目标申请的status字段,其余数据保持不变,既保证操作的原子性,又避免因多端并发操作导致的申请数据混乱,符合企业审批“状态单一来源、更新可追溯”的核心要求。

自动审批逻辑:

应用通过useEffect实现每分钟一次的自动审批逻辑,这一核心逻辑在鸿蒙系统中稳定运行的关键在于:

useEffect(() => {
  const interval = setInterval(() => {
    const randomApplication =补卡申请s[Math.floor(Math.random() *补卡申请s.length)];
    if (randomApplication.status === 'pending') {
      const updatedApplications =补卡申请s.map(app => 
        app.id === randomApplication.id ? { ...app, status: Math.random() > 0.5 ? 'approved' : 'rejected' } : app
      );
      set补卡申请s(updatedApplications);
      Alert.alert('审批通知', `补卡申请已${Math.random() > 0.5 ? '批准' : '拒绝'}`);
    }
  }, 60000);

  return () => clearInterval(interval);
}, [补卡申请s]);

setInterval/clearInterval是React Native封装的通用定时器API,已适配鸿蒙的任务调度机制,不会因鸿蒙系统的后台任务管理策略导致定时器失效;useEffect的依赖数组包含补卡申请s,保证申请列表变更时重新创建定时器,适配鸿蒙组件的更新生命周期;返回的清理函数对应鸿蒙组件onDestroy生命周期,确保组件卸载时销毁定时器,避免鸿蒙设备出现内存泄漏。自动审批逻辑中仅处理pending状态的申请,符合企业审批“未审批才处理”的业务规则,且通过随机数模拟审批结果,精准还原企业审批的随机性场景,同时状态更新完全遵循TypeScript的枚举类型约束,避免跨端运行时错误。

审批流程的跨端闭环

handleSubmit补卡申请函数是补卡申请提交的核心入口,其内部首先校验“员工选中状态+补卡日期+补卡原因”的完整性,这是补卡申请的核心校验逻辑,保证申请数据的合规性;随后构建符合TypeScript类型约束的新申请对象,通过不可变更新添加到状态中,确保鸿蒙分布式数据环境下的同步准确性。

handleApprove补卡handleReject补卡函数则实现了审批操作的核心逻辑,分别将目标申请的status更新为approvedrejected,并通过Alert推送审批结果提示。这两个函数仅修改状态字段,不改变员工关联、补卡日期等核心数据,既保证审批操作的精准性,又适配鸿蒙系统的轻量级数据更新策略,避免因大量数据传输导致的跨端性能问题。


应用的UI层基于React Native的StyleSheet统一管理样式,既保证鸿蒙系统中的原生渲染效果,又兼顾补卡申请这类审批应用对状态可视化、操作辨识度的特殊要求。

样式

StyleSheet将CSS样式抽象为跨平台的样式对象,核心样式属性在鸿蒙系统中会被精准转换为ArkUI的布局属性,同时针对审批场景设计了差异化的视觉样式:

const styles = StyleSheet.create({
  section: {
    backgroundColor: '#ffffff',
    marginHorizontal: 16,
    borderRadius: 12,
    padding: 16,
    // 阴影跨端适配:elevation适配鸿蒙/Android,shadow系列适配iOS
    elevation: 2,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
  },
  approveButton: {
    backgroundColor: '#10b981', // 绿色系批准按钮,符合企业审批的视觉认知
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
    marginRight: 8,
  },
  rejectButton: {
    backgroundColor: '#ef4444', // 红色系拒绝按钮,强化操作辨识度
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
  },
  selectedCard: {
    borderWidth: 2,
    borderColor: '#0284c7', // 统一的选中态边框,适配员工选择的可视化
  }
});

其中,elevation属性在鸿蒙系统中会被解析为原生阴影层级,borderRadius适配鸿蒙的圆角渲染规则,保证UI视觉效果的跨端一致性;批准/拒绝按钮采用绿色/红色的差异化色彩体系,既符合企业审批场景的视觉认知习惯,又适配鸿蒙系统的色彩渲染特性,避免因色彩空间差异导致的视觉混淆;selectedCard样式为员工选择项提供明确的选中态反馈,符合审批应用“选择可识别、操作可追溯”的核心要求。

交互组件

核心交互逻辑上,仅对statuspending的申请展示批准/拒绝按钮,实现了基于状态的交互控制,适配补卡审批“仅待审批申请可操作”的业务规则;TouchableOpacity组件在鸿蒙系统中渲染为具备原生点击反馈的按钮,保证审批操作的交互质感;Modal弹窗展示补卡申请的完整详情,包括员工信息、补卡日期、原因、状态等,通过可选链操作符(employee?.name)处理空值情况,避免因关联ID错误导致的跨端运行时错误,符合鸿蒙系统对应用稳定性的严苛要求。


当前代码已实现基础的鸿蒙跨端兼容,在生产环境中,可针对鸿蒙系统特性和补卡审批业务需求进行深度优化,进一步提升应用的企业级服务能力:

1. 高性能列表

应用中补卡申请列表采用ScrollView + map的方式渲染,在鸿蒙系统中面对大量审批数据时可能出现卡顿。可替换为React Native的FlatList组件,该组件在鸿蒙系统中会适配ArkUI的List原生组件,实现按需渲染和组件复用,通过getItemLayout优化列表滚动性能,尤其适合展示企业月度/年度的补卡申请记录和审批统计数据。

2. 鸿蒙原生

补卡审批的核心诉求是流程标准化和结果实时同步,可通过React Native的Native Module机制封装鸿蒙原生能力:

  • 分布式数据同步:集成鸿蒙DistributedDataManager,实现补卡申请记录在管理员鸿蒙平板、员工鸿蒙手机、企业智慧屏之间的实时同步,确保审批状态的即时更新;
  • 审批流程自动化:封装鸿蒙WorkflowKit原生API,将补卡申请流程与企业现有审批流对接,实现审批节点的自动流转和权限管控;
  • 系统级通知推送:利用鸿蒙NotificationKit,向员工推送审批结果的系统级通知,即使应用后台运行也能保证通知触达,提升审批效率。

3. 审批数据智能化管理

基于鸿蒙的AI能力和React Native的状态管理,可实现补卡申请的智能化管理:

  • 通过鸿蒙的文本分析能力,自动识别补卡原因的合理性,对异常原因(如重复补卡、无理由补卡)进行标记,辅助管理员审批;
  • 结合鸿蒙的数据分析能力,统计各部门补卡申请的频次和审批通过率,生成考勤异常分析报表,为企业考勤制度优化提供数据支撑;
  • 利用鸿蒙的分布式权限管理,为不同层级管理员分配对应部门的补卡审批权限,实现精细化权限管控。

这款基于React Native开发的补卡申请应用,通过枚举型状态设计的强类型数据模型、React Hooks状态管理和通用UI组件设计,构建了具备完整鸿蒙跨端兼容能力的企业审批应用架构,核心技术要点可总结为:

  • 通用API选型是实现鸿蒙兼容的基础,基于React Native通用组件构建核心逻辑,规避平台专属代码,保证了补卡申请UI和交互的跨端一致性;
  • TypeScript枚举型状态约束适配鸿蒙ArkTS的静态类型特性,避免跨端数据交互中的状态错误,保障审批流程的标准化和可追溯性;
  • React Hooks不可变更新策略与鸿蒙组件生命周期深度融合,实现审批状态的精准更新,保障了核心补卡审批流程的跨端稳定运行;
  • 统一的StyleSheet样式系统实现了UI在鸿蒙设备上的原生渲染,差异化的审批按钮色彩设计兼顾了操作辨识度和企业审批场景的视觉规范。

在企业考勤管理中,员工忘记打卡的情况时有发生,如何高效处理补卡申请成为人力资源管理的重要环节。本文将深入剖析一个基于 React Native 构建的补卡申请应用,探讨其技术实现细节及鸿蒙跨端能力的应用。

技术选型

该应用采用了现代 React Native 函数式组件架构,通过 TypeScript 类型系统和 React Hooks 实现了一个功能完整的补卡申请管理系统。核心技术栈包括:

  • React Native:作为跨端开发框架,提供了统一的组件 API,确保应用在 iOS、Android 及鸿蒙平台上的一致性体验
  • TypeScript:通过严格的类型定义增强代码可维护性,明确了数据结构和组件接口
  • React Hooks:使用 useState 管理应用状态,useEffect 处理副作用逻辑,实现了声明式的状态管理
  • Base64 图标:采用 Base64 编码的图标资源,避免了不同平台资源格式的差异,提高了跨端兼容性
  • 响应式布局:使用 Dimensions API 获取屏幕尺寸,实现适配不同设备的响应式界面

数据模型

应用通过 TypeScript 接口定义了两个核心数据类型,构建了完整的补卡申请数据模型体系:

// 员工类型
type Employee = {
  id: string;
  name: string;
  department: string;
  position: string;
};

// 补卡申请类型
type补卡申请 = {
  id: string;
  employeeId: string;
  date: string;
  reason: string;
  status: 'pending' | 'approved' | 'rejected';
};

这种强类型设计不仅提高了代码可读性,也为鸿蒙跨端适配提供了清晰的数据契约,确保不同平台间数据传递的一致性。数据模型的设计充分考虑了补卡申请的核心要素,包含了员工信息、补卡日期、原因和审批状态等关键数据。

状态管理

应用使用 useState Hook 管理多个复杂状态,包括员工列表、补卡申请、选中状态等:

const [employees] = useState<Employee[]>([
  {
    id: '1',
    name: '李先生',
    department: '技术部',
    position: '前端工程师'
  },
  {
    id: '2',
    name: '王女士',
    department: '市场部',
    position: '市场专员'
  }
]);

// 其他状态定义...

特别值得注意的是,应用通过 useEffect 实现了补卡申请的自动审批机制:

// 自动审批补卡申请
useEffect(() => {
  const interval = setInterval(() => {
    const randomApplication =补卡申请s[Math.floor(Math.random() *补卡申请s.length)];
    if (randomApplication.status === 'pending') {
      const updatedApplications =补卡申请s.map(app =>
        app.id === randomApplication.id ? { ...app, status: Math.random() > 0.5 ? 'approved' : 'rejected' } : app
      );
      set补卡申请s(updatedApplications);
      Alert.alert('审批通知', `补卡申请已${Math.random() > 0.5 ? '批准' : '拒绝'}`);
    }
  }, 60000);

  return () => clearInterval(interval);
}, [补卡申请s]);

这种基于时间间隔的自动审批机制,模拟了真实场景中管理员审批补卡申请的过程,为考勤管理提供了自动化的技术支持。同时,通过 useEffect 的清理函数,确保了定时器在组件卸载时被正确清除,避免了内存泄漏。


在 React Native 鸿蒙跨端开发中,该应用体现了以下关键技术点:

  1. 组件兼容性:使用 React Native 核心组件(如 SafeAreaView、View、Text、TouchableOpacity、ScrollView、Modal 等),确保在鸿蒙系统上的兼容性
  2. 资源管理:通过 Base64 编码的图标资源,避免了不同平台资源格式的差异,提高了跨端部署的一致性
  3. 尺寸适配:使用 Dimensions API 获取屏幕尺寸,实现响应式布局,适应不同设备屏幕
  4. 状态管理:采用 React Hooks 进行状态管理,保持跨平台代码一致性
  5. 类型安全:TypeScript 类型定义确保了数据结构在不同平台间的一致性
  6. API 调用:使用 React Native 统一的 API 调用方式,如 Alert 组件,确保在鸿蒙平台上的正确显示
  7. 中文命名支持:代码中使用了中文变量名和类型名,展示了 React Native 对中文命名的良好支持,这在鸿蒙等中文生态系统中尤为重要

员工管理与选择

应用实现了员工的选择功能,用户可以选择特定员工提交补卡申请:

const handleSelectEmployee = (employeeId: string) => {
  setSelectedEmployee(employeeId);
  Alert.alert('选择员工', '您已选择该员工进行补卡申请');
};

补卡申请提交

应用提供了补卡申请的提交功能,员工可以输入补卡日期和原因:

const handleSubmit补卡申请 = () => {
  if (new补卡申请.date && new补卡申请.reason && selectedEmployee) {
    const newApplication:补卡申请 = {
      id: (补卡申请s.length + 1).toString(),
      employeeId: selectedEmployee,
      date: new补卡申请.date,
      reason: new补卡申请.reason,
      status: 'pending'
    };
    set补卡申请s([...补卡申请s, newApplication]);
    setNew补卡申请({ date: '', reason: '' });
    Alert.alert('提交成功', '新的补卡申请已提交');
  } else {
    Alert.alert('提示', '请选择员工并填写完整的补卡信息');
  }
};

补卡申请审批

应用实现了补卡申请的审批功能,管理员可以批准或拒绝补卡申请:

const handleApprove补卡 = (applicationId: string) => {
  const updatedApplications =补卡申请s.map(app =>
    app.id === applicationId ? { ...app, status: 'approved' } : app
  );
  set补卡申请s(updatedApplications);
  Alert.alert('审批成功', '补卡申请已批准');
};

const handleReject补卡 = (applicationId: string) => {
  const updatedApplications =补卡申请s.map(app =>
    app.id === applicationId ? { ...app, status: 'rejected' } : app
  );
  set补卡申请s(updatedApplications);
  Alert.alert('审批成功', '补卡申请已拒绝');
};

申请查看功能

应用提供了补卡申请的查看功能,通过模态框展示详细信息:

const handleViewApplication = (applicationId: string) => {
  const application =补卡申请s.find(a => a.id === applicationId);
  if (application) {
    const employee = employees.find(e => e.id === application.employeeId);
    setModalContent(`员工: ${employee?.name}\n部门: ${employee?.department}\n职位: ${employee?.position}\n日期: ${application.date}\n原因: ${application.reason}\n状态: ${application.status}`);
    setIsModalVisible(true);
  }
};

自动审批机制

应用通过定时任务自动审批补卡申请,模拟了真实的管理审批流程。


应用的 UI 设计遵循了现代移动应用的设计原则,使用了以下组件和交互模式:

  • 安全区域:通过 SafeAreaView 确保内容显示在安全区域内,适应不同设备的屏幕刘海和底部指示条
  • 滚动视图:通过 ScrollView 实现内容的垂直滚动,适应不同长度的员工列表和补卡申请
  • 卡片布局:使用 TouchableOpacity 和 View 组合实现卡片式列表项,提供清晰的视觉层次和交互反馈
  • 表单输入:通过 TextInput 组件实现补卡信息的输入
  • 模态框:通过 Modal 组件展示详细信息,如补卡申请详情
  • 交互反馈:使用 Alert 组件提供操作反馈和审批通知
  • 响应式设计:根据屏幕尺寸动态调整布局,确保在不同设备上的良好显示效果

  1. 跨端架构:基于 React Native 构建,实现了一次编码多平台运行的目标,特别关注了鸿蒙平台的适配
  2. 类型安全:全面使用 TypeScript 类型定义,提高代码质量和可维护性,确保补卡数据的准确性
  3. 自动化审批:通过定时任务自动审批补卡申请,提高了考勤管理的效率
  4. 智能状态管理:通过 React Hooks 实现了简洁的状态管理,提高了代码的可读性和可维护性
  5. 模块化设计:通过清晰的类型定义和函数划分,实现了代码的模块化,提高了可维护性
  6. 实时数据反馈:通过即时的 Alert 反馈,增强用户操作体验
  7. 数据结构设计:通过关联的数据结构,如补卡申请关联员工,实现了复杂人事数据的有效组织
  8. 中文命名支持:代码中使用了中文变量名和类型名,展示了 React Native 对中文命名的良好支持,这在鸿蒙等中文生态系统中尤为重要
  9. 状态流转:通过明确的状态定义(pending、approved、rejected),实现了补卡申请的完整生命周期管理

在实际应用中,还可以考虑以下性能优化策略:

  1. 状态管理优化:对于大型应用,可以考虑使用 Redux 或 Context API 进行全局状态管理,提高状态更新的效率
  2. 组件拆分:将大型组件拆分为更小的可复用组件,提高渲染性能和代码可维护性
  3. 数据缓存:对员工数据和补卡申请进行本地缓存,减少重复计算和网络请求
  4. 动画性能:使用 React Native 的 Animated API 实现流畅的过渡动画,提升用户体验
  5. 内存管理:确保及时清理不再使用的状态和事件监听器,避免内存泄漏
  6. 网络优化:对于实际应用中的远程数据同步,实现合理的网络请求策略,如批量上传、增量同步等
  7. 计算优化:对于补卡数据的统计和分析,可以考虑使用 memoization 技术缓存计算结果
  8. 列表优化:对于长列表,使用 FlatList 组件替代 ScrollView,提高渲染性能

在开发过程中,可能面临的技术挑战及解决方案:

  1. 鸿蒙平台适配:通过使用 React Native 核心组件和统一的 API 调用方式,确保应用在鸿蒙平台上的兼容性
  2. 实时数据同步:在实际应用中,可以实现与后端服务器的实时数据同步,确保补卡申请数据的一致性
  3. 审批流程复杂化:可以实现更复杂的审批流程,如多级审批、审批意见记录等
  4. 数据安全:实现人事数据的加密存储和传输,保护企业数据安全
  5. 离线功能:实现基本的离线操作能力,确保在网络不稳定情况下的正常使用
  6. 性能优化:针对不同设备性能差异,实现自适应的性能优化策略,确保在中低端设备上的流畅运行
  7. 用户体验一致性:确保在不同平台上的用户体验一致,特别是交互方式和视觉效果
  8. 多语言支持:实现多语言支持,满足不同地区企业的需求

通过对这个补卡申请应用的技术解读,我们可以看到 React Native 在跨端开发中的强大能力。该应用不仅实现了完整的补卡申请管理功能,还展示了如何通过 TypeScript、React Hooks 等现代前端技术构建高质量的跨端应用。


真实演示案例代码:







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

// Base64 图标库
const ICONS_BASE64 = {
 补卡: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  审批: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  日历: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
  用户: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
};

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

// 员工类型
type Employee = {
  id: string;
  name: string;
  department: string;
  position: string;
};

// 补卡申请类型
type补卡申请 = {
  id: string;
  employeeId: string;
  date: string;
  reason: string;
  status: 'pending' | 'approved' | 'rejected';
};

// 补卡申请应用组件
const补卡申请App: React.FC = () => {
  const [employees] = useState<Employee[]>([
    {
      id: '1',
      name: '李先生',
      department: '技术部',
      position: '前端工程师'
    },
    {
      id: '2',
      name: '王女士',
      department: '市场部',
      position: '市场专员'
    }
  ]);

  const [补卡申请s, set补卡申请s] = useState<补卡申请[]>([
    {
      id: '1',
      employeeId: '1',
      date: '2023-12-01',
      reason: '忘记打卡',
      status: 'pending'
    }
  ]);

  const [selectedEmployee, setSelectedEmployee] = useState<string | null>(null);
  const [new补卡申请, setNew补卡申请] = useState({
    date: '',
    reason: ''
  });
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [modalContent, setModalContent] = useState('');

  // 自动审批补卡申请
  useEffect(() => {
    const interval = setInterval(() => {
      const randomApplication =补卡申请s[Math.floor(Math.random() *补卡申请s.length)];
      if (randomApplication.status === 'pending') {
        const updatedApplications =补卡申请s.map(app => 
          app.id === randomApplication.id ? { ...app, status: Math.random() > 0.5 ? 'approved' : 'rejected' } : app
        );
        set补卡申请s(updatedApplications);
        Alert.alert('审批通知', `补卡申请已${Math.random() > 0.5 ? '批准' : '拒绝'}`);
      }
    }, 60000);

    return () => clearInterval(interval);
  }, [补卡申请s]);

  const handleSelectEmployee = (employeeId: string) => {
    setSelectedEmployee(employeeId);
    Alert.alert('选择员工', '您已选择该员工进行补卡申请');
  };

  const handleSubmit补卡申请 = () => {
    if (new补卡申请.date && new补卡申请.reason && selectedEmployee) {
      const newApplication:补卡申请 = {
        id: (补卡申请s.length + 1).toString(),
        employeeId: selectedEmployee,
        date: new补卡申请.date,
        reason: new补卡申请.reason,
        status: 'pending'
      };
      set补卡申请s([...补卡申请s, newApplication]);
      setNew补卡申请({ date: '', reason: '' });
      Alert.alert('提交成功', '新的补卡申请已提交');
    } else {
      Alert.alert('提示', '请选择员工并填写完整的补卡信息');
    }
  };

  const handleApprove补卡 = (applicationId: string) => {
    const updatedApplications =补卡申请s.map(app => 
      app.id === applicationId ? { ...app, status: 'approved' } : app
    );
    set补卡申请s(updatedApplications);
    Alert.alert('审批成功', '补卡申请已批准');
  };

  const handleReject补卡 = (applicationId: string) => {
    const updatedApplications =补卡申请s.map(app => 
      app.id === applicationId ? { ...app, status: 'rejected' } : app
    );
    set补卡申请s(updatedApplications);
    Alert.alert('审批成功', '补卡申请已拒绝');
  };

  const handleViewApplication = (applicationId: string) => {
    const application =补卡申请s.find(a => a.id === applicationId);
    if (application) {
      const employee = employees.find(e => e.id === application.employeeId);
      setModalContent(`员工: ${employee?.name}\n部门: ${employee?.department}\n职位: ${employee?.position}\n日期: ${application.date}\n原因: ${application.reason}\n状态: ${application.status}`);
      setIsModalVisible(true);
    }
  };

  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}>若员工忘记打卡,可在App中提交补卡申请并说明原因,由管理员审批</Text>
      </View>

      <ScrollView style={styles.content}>
        {/* 员工列表 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>员工列表</Text>
          {employees.map(employee => (
            <TouchableOpacity 
              key={employee.id}
              style={[
                styles.card,
                selectedEmployee === employee.id && styles.selectedCard
              ]}
              onPress={() => handleSelectEmployee(employee.id)}
            >
              <Text style={styles.icon}>👤</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>{employee.name}</Text>
                <Text style={styles.cardDescription}>部门: {employee.department}</Text>
                <Text style={styles.cardDescription}>职位: {employee.position}</Text>
              </View>
            </TouchableOpacity>
          ))}
        </View>

        {/* 提交补卡申请 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>提交补卡申请</Text>
          <View style={styles.inputRow}>
            <TextInput
              style={styles.input}
              placeholder="补卡日期 (YYYY-MM-DD)"
              value={new补卡申请.date}
              onChangeText={(text) => setNew补卡申请({ ...new补卡申请, date: text })}
            />
            <TextInput
              style={styles.input}
              placeholder="补卡原因"
              value={new补卡申请.reason}
              onChangeText={(text) => setNew补卡申请({ ...new补卡申请, reason: text })}
            />
          </View>
          <TouchableOpacity 
            style={styles.addButton}
            onPress={handleSubmit补卡申请}
          >
            <Text style={styles.addText}>提交补卡申请</Text>
          </TouchableOpacity>
        </View>

        {/* 补卡申请列表 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>补卡申请列表</Text>
          {补卡申请s.map(application => (
            <TouchableOpacity 
              key={application.id}
              style={styles.applicationCard}
              onPress={() => handleViewApplication(application.id)}
            >
              <Text style={styles.icon}>📅</Text>
              <View style={styles.cardInfo}>
                <Text style={styles.cardTitle}>申请ID: {application.id}</Text>
                <Text style={styles.cardDescription}>日期: {application.date}</Text>
                <Text style={styles.cardDescription}>状态: {application.status}</Text>
              </View>
              {application.status === 'pending' && (
                <>
                  <TouchableOpacity 
                    style={styles.approveButton}
                    onPress={() => handleApprove补卡(application.id)}
                  >
                    <Text style={styles.approveText}>批准</Text>
                  </TouchableOpacity>
                  <TouchableOpacity 
                    style={styles.rejectButton}
                    onPress={() => handleReject补卡(application.id)}
                  >
                    <Text style={styles.rejectText}>拒绝</Text>
                  </TouchableOpacity>
                </>
              )}
            </TouchableOpacity>
          ))}
        </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',
  },
  applicationCard: {
    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,
  },
  inputRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 12,
  },
  input: {
    flex: 1,
    backgroundColor: '#f0f9ff',
    borderRadius: 8,
    paddingHorizontal: 12,
    paddingVertical: 8,
    fontSize: 14,
    color: '#0c4a6e',
    marginRight: 8,
  },
  addButton: {
    backgroundColor: '#0284c7',
    padding: 12,
    borderRadius: 8,
    alignItems: 'center',
  },
  addText: {
    color: '#ffffff',
    fontSize: 14,
    fontWeight: '500',
  },
  approveButton: {
    backgroundColor: '#10b981',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
    marginRight: 8,
  },
  approveText: {
    color: '#ffffff',
    fontSize: 12,
    fontWeight: '500',
  },
  rejectButton: {
    backgroundColor: '#ef4444',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 8,
  },
  rejectText: {
    color: '#ffffff',
    fontSize: 12,
    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补卡申请App;

请添加图片描述


打包

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

在这里插入图片描述

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

在这里插入图片描述

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

请添加图片描述
本文介绍了基于React Native和鸿蒙系统开发的跨平台补卡申请应用。应用采用React Native通用API体系,通过组件映射实现鸿蒙原生交互体验,并利用TypeScript强类型设计确保数据模型跨端兼容。核心功能包括自动审批、申请提交和审批操作,均基于React Hooks实现,适配鸿蒙分布式数据管理。样式系统通过StyleSheet统一管理,确保多终端视觉一致性。该方案实现了企业考勤审批流程的标准化、状态可追踪和跨设备协同,展示了React Native与鸿蒙生态深度融合的技术实践。

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐