UniApp迁移实施文档.md 13 KB

min_App_doctor 转 UniApp 迁移实施文档

1. 目标与范围

  • 目标:将 min_App_doctor(微信原生小程序)迁移为 UniApp(Vue3 + uni-ui/uv-ui 可选)工程,优先保证微信小程序端可运行,再扩展 H5 / App。
  • 范围:覆盖当前 app.json 已注册的 58 个页面、公共组件、网络层、登录态、IM(TIM)、音视频(TRTC)、处方/录入业务流程。
  • 输出目录:min_App_doctor_uniapp/

2. 现状盘点(源项目)

  • 路由:主包 + 3 个分包(myConsultRoomentryWriteoneselfCenter)。
  • 依赖:
    • @vant/weapp
    • tim-wx-sdk
    • cos-wx-sdk-v5
    • weui-miniprogram
  • 核心特征:
    • 大量 wx.* 平台 API 调用(登录、存储、路由、权限、网络、系统信息)。
    • Page/Component/Behavior 组织方式。
    • 统一请求入口:config/https.js(含 session 注入、错误码处理、加解密分支)。
    • IM 逻辑集中在 utils/message.js(TIM 登录、收消息、发送业务事件消息)。

3. 迁移技术方案

3.1 UniApp 技术栈建议

  • 框架:uni-app xuni-app(推荐先用 uni-app + Vue3 + Vite)。
  • 语言:Vue3 + <script setup> + TypeScript(可先 JS 后 TS)。
  • 状态:Pinia
  • 请求:uni.request 二次封装(兼容原 $http.post 语义)。
  • UI:
    • 原生标签 + 自定义组件优先;
    • 表单复杂处可选 uv-ui
    • 不建议继续强依赖 @vant/weapp
  • IM:继续使用腾讯 IM SDK(选择 UniApp/小程序可用版本)。
  • 音视频:TRTC 需按 UniApp + 微信小程序能力重新接入(优先保留微信端能力)。

3.2 目录重构建议

min_App_doctor_uniapp/
  src/
    App.vue
    main.ts
    pages.json
    manifest.json
    uni.scss
    pages/
      tabBar/
      components/
      myConsultRoom/
      entryWrite/
      oneselfCenter/
    components/
    store/
    api/
      http.ts
      modules/
    utils/
      auth.ts
      message.ts
      common.ts
      crypto/
  package.json

4. 路由迁移(app.json -> pages.json)

4.1 迁移原则

  • app.json.pages -> pages.json.pages
  • app.json.subPackages -> pages.json.subPackages
  • tabBar 原样迁移到 pages.json.tabBar
  • 路由跳转统一替换为 uni.navigateTo / uni.redirectTo / uni.switchTab / uni.reLaunch

4.2 pages.json 模板(可直接落地)

{
  "pages": [
    { "path": "pages/tabBar/home/home", "style": { "navigationBarTitleText": "工作台" } },
    { "path": "pages/tabBar/login/login", "style": { "navigationBarTitleText": "我的" } },
    { "path": "pages/tabBar/personalcenter/personalcenter", "style": { "navigationBarTitleText": "个人中心" } },
    { "path": "pages/tabBar/messageCenter/messageCenter", "style": { "navigationBarTitleText": "消息" } },
    { "path": "pages/components/searchSelect/searchSelect", "style": {} },
    { "path": "pages/components/mesicalRcordSelect/mesicalRcordSelect", "style": {} },
    { "path": "pages/tabBar/personalcenter/locList/locList", "style": {} },
    { "path": "pages/tabBar/personalcenter/doctorCard/doctorCard", "style": {} },
    { "path": "pages/tabBar/faceLogin/faceLogin", "style": {} },
    { "path": "pages/components/gallery/gallery", "style": {} }
  ],
  "subPackages": [
    {
      "root": "pages/myConsultRoom",
      "pages": [
        { "path": "page/MyLoc/MyLoc", "style": {} },
        { "path": "page/allPatient/allPatient", "style": {} },
        { "path": "page/searchPatient/searchPatient", "style": {} },
        { "path": "page/treatment/treatment", "style": {} },
        { "path": "page/treatment/messageTemplate/messageTemplate", "style": {} },
        { "path": "page/graphic/graphic", "style": {} },
        { "path": "page/graphicDateil/graphicDateil", "style": {} },
        { "path": "page/refusal/refusal", "style": {} },
        { "path": "page/treatmentrecord/treatmentrecord", "style": {} },
        { "path": "page/trtcRoom/trtcRoom", "style": {} },
        { "path": "page/trtc-room/trtc-room", "style": {} },
        { "path": "page/historyTreatment/historyTreatment", "style": {} },
        { "path": "page/InviteDoctor/InviteDoctor", "style": {} }
      ]
    },
    {
      "root": "pages/entryWrite",
      "pages": [
        { "path": "page/Entry/Entry", "style": {} },
        { "path": "page/Entry/diagnostic/diagnostic", "style": {} },
        { "path": "page/Entry/diagnostic/addDiagnostic/addDiagnostic", "style": {} },
        { "path": "page/Entry/diagnostic/addDiagnostic/customDiagnostics/customDiagnostics", "style": {} },
        { "path": "page/Entry/diagnostic/addDiagnostic/historicalDiagnosis/historicalDiagnosis", "style": {} },
        { "path": "page/Entry/diagnostic/addDiagnostic/templateDiagnostic/templateDiagnostic", "style": {} },
        { "path": "page/Entry/prescription/prescription", "style": {} },
        { "path": "page/Entry/prescription/addPrescription/addPrescription", "style": {} },
        { "path": "page/Entry/medicalRecord/medicalRecord", "style": {} },
        { "path": "page/Entry/prescription/addPrescription/customDiagnostics/grass/grassPush/grassPush", "style": {} },
        { "path": "page/Entry/prescription/prescDetail/prescDetail", "style": {} },
        { "path": "page/Entry/prescription/prescAuditDetail/prescAuditDetail", "style": {} },
        { "path": "page/Entry/prescription/handlePrescAudit/handlePrescAudit", "style": {} },
        { "path": "page/Entry/prescription/prescProcess/prescProcess", "style": {} },
        { "path": "page/Entry/prescription/addExamine/addExamine", "style": {} },
        { "path": "page/Entry/prescription/addExamine/completeExamine/completeExamine", "style": {} },
        { "path": "page/Entry/prescription/addInspection/addInspection", "style": {} },
        { "path": "page/Entry/prescription/addInspection/completeInspection/completeInspection", "style": {} }
      ]
    },
    {
      "root": "pages/oneselfCenter",
      "pages": [
        { "path": "page/help/help", "style": {} },
        { "path": "page/history/history", "style": {} },
        { "path": "page/scheduling/scheduling", "style": {} },
        { "path": "page/schedulingHandle/schedulingHandle", "style": {} },
        { "path": "page/schedulingQuery/schedulingQuery", "style": {} },
        { "path": "page/comprehensiveScore/comprehensiveScore", "style": {} },
        { "path": "page/finishAdmRecord/finishAdmRecord", "style": {} },
        { "path": "page/feedback/feedback", "style": {} },
        { "path": "page/feedbackSuc/feedbackSuc", "style": {} },
        { "path": "page/helppdf/helppdf", "style": {} },
        { "path": "page/replyTemplate/replyTemplate", "style": {} },
        { "path": "page/replyTemplateHand/replyTemplateHand", "style": {} },
        { "path": "page/schedulingRange/schedulingRange", "style": {} },
        { "path": "page/notice/notice", "style": {} },
        { "path": "page/addNotice/addNotice", "style": {} },
        { "path": "page/batchScheduling/batchScheduling", "style": {} },
        { "path": "page/batchSchedullingHandle/batchSchedullingHandle", "style": {} }
      ]
    }
  ],
  "tabBar": {
    "color": "#666666",
    "selectedColor": "#1C86EE",
    "borderStyle": "white",
    "backgroundColor": "#fff",
    "list": [
      { "pagePath": "pages/tabBar/home/home", "text": "工作台", "iconPath": "static/images/workbench.png", "selectedIconPath": "static/images/workbenchS.png" },
      { "pagePath": "pages/tabBar/messageCenter/messageCenter", "text": "消息", "iconPath": "static/images/message.png", "selectedIconPath": "static/images/messageS.png" },
      { "pagePath": "pages/tabBar/login/login", "text": "我的", "iconPath": "static/images/mine.png", "selectedIconPath": "static/images/mineS.png" }
    ]
  },
  "globalStyle": {
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "black",
    "backgroundTextStyle": "light"
  }
}

5. 代码级迁移规则

5.1 生命周期

  • App({ onLaunch/onShow/onHide }) -> App.vue + onLaunch/onShow/onHide
  • Page({...}) -> Vue SFConLoad/onShow/onHide/onUnload
  • Component({...}) -> Vue 组件props + emits + ref)。
  • Behavior -> composables(如 useSafeArea/useEventsMixin)。

5.2 API 替换清单(重点)

  • wx.request -> uni.request
  • wx.navigateTo -> uni.navigateTo
  • wx.switchTab -> uni.switchTab
  • wx.reLaunch -> uni.reLaunch
  • wx.getStorageSync/setStorageSync/removeStorageSync -> uni.getStorageSync/setStorageSync/removeStorageSync
  • wx.showToast/showModal/showLoading/hideLoading -> uni.showToast/showModal/showLoading/hideLoading
  • wx.getSystemInfoSync/getWindowInfo -> uni.getSystemInfoSync
  • wx.login -> uni.login(微信小程序端)
  • wx.setNavigationBarTitle -> uni.setNavigationBarTitle

5.3 条件编译建议

// #ifdef MP-WEIXIN
uni.login({ provider: 'weixin', success: () => {} })
// #endif
  • 对 IM、TRTC、摄像头、人脸、授权等高平台耦合能力必须加条件编译。

6. 网络层迁移(原 $http.post)

6.1 保持行为一致

  • 入参保持:code + data + success + fail
  • 自动注入 session(来自 userData)。
  • 保留错误码拦截:
    • 未登录码跳转登录页。
    • token/session 失效清理缓存。
    • 网络错误统一提示。
  • 保留加密分支(urlAddressEncrypt 时 AES 加解密)。

6.2 建议实现结构

  • src/api/http.ts:通用 post(code, data, options)
  • src/api/modules/*.ts:按业务拆分(登录、问诊、录入、排班、公告等)。
  • src/store/auth.ts:维护 userData / appInfo / TIMInfo / patientData

7. IM/TRTC 迁移要点

  • utils/message.js 迁到 src/utils/message.ts,保留:
    • TIM 初始化、登录、登出;
    • 消息接收回调;
    • 业务消息发送(I/B/D/F/P 等事件)。
  • TRTC 页面(trtcRoomtrtc-room)建议优先迁微信小程序端版本,H5/App 作为二期。
  • 登录被踢、邀请会诊、消息落库逻辑需回归测试。

8. UI 组件迁移策略

  • wxml/wxss/js 三件套 -> 单文件组件 .vue
  • 页面级迁移顺序(建议):
    1. 登录/个人中心/工作台
    2. 图文咨询 + 问诊会话
    3. 录入模块(Entry)
    4. 排班/公告/模板运营页
  • @vant/weapp 组件:
    • 能替换为 uni-ui/uv-ui 的直接替换;
    • 高度定制组件改写为本地 Vue 组件。

9. 分阶段实施计划

Phase 1:工程初始化(1-2 天)

  • 创建 UniApp 项目骨架。
  • 搭建 pages.json/tabBar/subPackages
  • 建立 api/store/utils/components 基础目录。

Phase 2:底座能力迁移(2-4 天)

  • 完成 http.ts、鉴权、缓存、全局错误处理。
  • 迁移 app.js 的启动逻辑(getAppInfo/autoUpdate)。
  • 建立 IM 基础连接与登录流程。

Phase 3:核心业务迁移(7-15 天)

  • 首页、消息、我的、图文问诊、录入主流程。
  • 完成关键组件:navbar、患者列表、诊断/处方编辑。

Phase 4:长尾页面迁移(5-10 天)

  • 排班、公告、帮助反馈、模板、历史记录等。
  • 未注册但源码存在的 9 个页面按业务确认是否纳入。

Phase 5:联调与发布(3-5 天)

  • 全链路回归(登录 -> 问诊 -> 录入 -> 完成/消息回流)。
  • 性能与分包体积优化。
  • 灰度发布。

10. 风险与规避

  • 风险 1:TRTC/IM 在 UniApp 多端差异大。
    • 规避:先锁定 MP-WEIXIN 端打通,再扩端。
  • 风险 2:原有 Behavior/selectComponent 强耦合。
    • 规避:以 composable + provide/inject 重构。
  • 风险 3:历史页面很多,迁移成本高。
    • 规避:按主流程优先,长尾页面批次迁移。
  • 风险 4:隐式全局状态(getApp() + storage)难维护。
    • 规避:迁移时统一收敛到 Pinia。

11. 验收标准(必须满足)

  • 路由:58 个已注册页面可进入,tabBar 可用。
  • 业务主链路:
    • 登录成功并维持会话;
    • 图文咨询可进入问诊;
    • 录入(病历/诊断/医嘱)可保存;
    • 消息收发、邀请会诊正常;
    • 个人中心排班/公告/模板关键操作可用。
  • 稳定性:网络异常、登录失效、权限拒绝均有可见兜底提示。

12. 迁移执行清单(可直接勾选)

  • 初始化 UniApp 项目与依赖
  • 建立 pages.json(主包 + 3 分包 + tabBar)
  • 完成 http.ts(session、错误码、加密)
  • 完成登录态与全局状态(Pinia)
  • 迁移 home/login/messageCenter/personalcenter
  • 迁移 myConsultRoom 主流程页
  • 迁移 entryWrite 主流程页
  • 迁移 oneselfCenter 页面
  • 迁移 IM/TIM 逻辑并联调
  • 迁移 TRTC 页面并联调
  • 完成全量回归测试
  • 输出上线说明与回滚方案