Files
hospital-rescue-app-v2/.workbuddy/memory/MEMORY.md
T
2026-09-24 16:25:22 +08:00

10 KiB
Raw Blame History

医院救援 APPhospital-rescue-app-v2)· 项目长期约定

技术栈

  • uni-app xuvue + uts,不是 uni-app(vue)。页面后缀 .uvue,脚本 <script lang="uts" setup>
  • UI 组件库 Tmui4uni_modules/tmx-ui,全局配置在 main.utsdesignSize 375 / unit px / 主色 #2563EB / dark light)。
  • easycom 自动扫描 components/组件名/组件名.uvue(本项目自研组件统一 hr- 前缀)。

设计规范

  • 设计稿基准 375 × 812mobile-design/v2/hospital-rescue-ui-v2.html 为唯一视觉依据(35 屏)。
  • 设计稿 px = uvue pxuni-app x px 为逻辑像素),不要乘 2。
  • 主色 #2563EB;四色腕带分级:危重 #DC2626 / 重伤 #F59E0B / 轻伤 #10B981 / 死亡 #1F2937。
  • 底 #F3F4F6、卡片 #FFF、分割线 #F3F4F6 / #E5E7EB、文本 #1F2937 / #6B7280 / #9CA3AF。

角标(xBadge)几何 — 已定稿,别再改尺寸

  • 全站角标基准(用户指定):高/圆角 36rpx、字号 22.4rpx、红底白字;.xBadge-countAndLabel!important 覆盖。
  • 角标实际宽 28px,不是 18pxmin-width:36rpx 是内容盒最小值,还要加左右各 10rpx 内边距。
  • 定位走 hr-design.css 的「左锚定」:top:-4rpx; left:44rpx; right:auto; transform:none(复刻设计稿 left:calc(50%+6px)),配 .xBadge{padding:0!important} + 全链 overflow:visible!important。 实测只压 icon 6px25%),3/5 Tab 与两位数都成立;别改回 right 定位或库默认半出血(会盖住 icon 42%~100%)。
  • x-tabbar:offset="[0,5]",Tab 项高度写死 60px(内容约 53px,上下余量仅 ~3.5px)。

uvue 硬约束(写样式前必读)

  1. 不支持 ::before / ::after → 装饰一律用实体元素
  2. 不支持 CSS 变量 var() → 颜色写实
  3. 不使用 display:gridgap → flex + margin
  4. 只使用 class 选择器(不写后代选择器);复合选择器 .a.b 也不要用 → 选中态改用 JS 计算 style 字符串内联下发
  5. Vue API 必须显式 importimport { ref, computed } from 'vue'
  6. computed 有返回类型时必须写 computed(() : T => {}),不能写 computed() : T => {}
  7. 模板里调用的函数必须在 script 中定义,否则运行期崩 _ctx.xxx is not a function(编译期不一定报错)
  8. 页面生命周期是 uvue 全局函数,不要从 @dcloudio/uni-app import 显示/隐藏要用 onPageShow / onPageHide(不是 onShow/onHide
  9. onLoad 取参用下标 opt['id']res.contentstring|null|undefined 要先判空
  10. uni.showToasticon 是字面量联合类型('none'|'success'|'error'|'loading'…),封装时形参别写 string
  11. 不用 aspect-ratio;尺寸用 windowWidth 计算或用固定值
  12. 不用内联 <svg>;图形用 view/border-radius/x-circle-progress 等替代
  13. 不支持 @keyframes / animation(App 端编译期直接 ERROR,之前记的「可用」是错的); 也不支持 page / body 元素选择器(页面底色由 .hr-page 或页面根节点承担) → 动效一律改 setInterval + ref 驱动内联样式(参见 event/index 红点、warehouse/scan 与 hr-scan-cam 扫描线、study/center 与 wrong-book 手风琴);扫描步长取 1/帧数 才能精确命中端点
  14. 模板里不能直接调用 import 进来的函数:编译成 unref(fn)(x)App 端 Kotlin 报 找不到名称"invoke" / Function invocation 'xxx(...)' expected#error18 → 在页面 script 里包一层本文件定义的 wrapper 再给模板用
  15. UTS 不提升函数声明:被调用的函数必须写在调用点之前(computed 回调里最容易踩) → 扫描脚本 check-uts-order.js
  16. background-image 在 App 端url(...) 图片完全不支持(原生视图无背景图概念) → 铺底图/照片用 <image> 绝对定位铺满,或 DrawableContext 原生绘制; linear-gradient 支持但只有一种合法形态linear-gradient(<关键字方向>, <色1>, <色2>) —— 必须 3 参数、方向必须是关键字(to bottom right 等,不是 135deg)、恰好 2 个颜色 (不能带 % 色标、不能 3 色)flatten/vapor 模式下 background-image 直接失效。 → 扫描脚本 check-uvue-bg.js
  17. UTS 里 any = Kotlin 非空 Anynull 强转必崩: java.lang.NullPointerException: null cannot be cast to non-null type kotlin.Any → 想表达可空必须写 any | null不要对可空值写 as any(等于 as Any); UTSJSONObject.getAny(k) 键不存在返回 null(不抛),调用方要自己兜; 结构体可选字段(field?: any)本就是可空,赋值时别强转。 排查技巧:栈指向哪个字面量 return { 那一行 → 就是字面量里某个 as 强转炸了。

uni_modules 本地补丁清单(升级插件会被覆盖,务必回归)

文件 位置 现象 / 改法
uni_modules/tmx-ui/components/x-upload-file/x-upload-file.uvue covertList()(标记「⚠ 本地补丁(2026-09-24)」) defaultList 项不带 requestel.getAny("request") as anynull cannot be cast to non-null type kotlin.Any;改为 `let requestdata : any

分段切换(xRadioButton)— 全站 5 处已统一

  • 原「hr-row + hr-chip 横向滚动」筛选全部换成 x-radio-buttonstudy/center、study/wrong-book、 message/list、message/index、triage/treat-records。
  • RADIO_BUTTON = { id:string, icon?, title, disabled? }change 下发 (string, number) 与 x-tabs 不同、无类型坑。v-model 绑 string id,业务要序号时用 chip = computed(() : number => { const i = parseInt(chipId.value); return isNaN(i) ? 0 : i })
  • 列表直接写字面量数组(项目惯例 { id:'0', title:'x' } as RADIO_BUTTON),别再用 string[] + v-for 拼。
  • 必须去掉外层 <scroll-view :scroll-x>:组件是等宽 flex:1 分段,放进横向 scroll-view 会收窄、滑块定位错。
  • 圆角坑:滑块圆角 = parseInt(round) - parseInt(space)按 px 下发(只有外层 round 跟 rpx), round/space 传 rpx 时内外单位不一致 → 现配 height=64rpx space=4rpx round=22rpx(滑块呈胶囊,视觉 OK)。
  • 字号必须 ≤22rpx:等宽 4 段每段文本可用宽仅 60.75px,26rpx 加粗「心肺复苏 5」=63.9px 会截断 (无头 Chrome 实测),22rpx=54.1px89%)安全;改文案先跑 node-workspace/radiobtn-fit-test.html
  • 样式基线:bg-color #E5E7EB / active-color #2563EB / active-font-color #FFFFFF / font-color #6B7280

事件中心(pages/event/index

  • 用户定稿:只保留「进行中 / 历史」两个 Tab(各占 50%),原「待接收」Tab 与 P0 全屏推送 (60 秒倒计时 / 立即接收 / 稍后处理 / 卡片 pending 分支)已整体移除——别按旧设计稿加回来。
  • 紧急红点呼吸(dotStyle / startPulse)保留,触发只看数据的 urgent 字段(与 stage 无关)。
  • ⚠ 遗留(已告知用户,待决定):mock 里 e3/e4 仍是 stage:'pending'(页面上不可见); message/list 的「去接收 ›」仍跳事件中心,但进行中 Tab 看不到对应事件 —— 需把这两条改 'ongoing' 或删除。

三段式扫码作业页(批量处置 / 批量转运)

  • hr-scan-cam 默认不启动相机cameraOn=false):点击整块取景区 openCamera() 才开启, 未开启时显示居中引导「点击开启摄像头」;底部按钮文案 开启扫码 ⇄ 连续扫码中。 扫描线定时器随相机开关启停(openCamera/toggleCamera 里 startLaser/stopLaser),不要 onMounted 空跑。
  • 批量转运「接收医院」是折叠字段 .op-pick(医院名+床位+⌄),点击整行唤起 hr-mask + hr-sheet 弹层(与 victims/create 同一套类);选项为「医院名 + hospitalMeta 距离」两行, 满员医院名置灰(os-name-dim)+ 床位红字,点它弹 toast 且不关闭弹层,选可用医院才关。

目录约定

components/     自研通用组件(hr- 前缀,easycom
common/styles/hr-design.css   全局设计系统(App.uvue 里 @import
mock/types.uts  数据模型类型
mock/index.uts  mock 数据(换接口只改这里)
utils/role.uts  会话 + 双角色 TabBar + 页面权限
utils/nav.uts   统一跳转(含权限拦截)
utils/quiz.uts  答题状态(跨页共享)
pages/          页面(按模块分子目录:home/event/mine/course/study/triage/warehouse/message/design

双角色

  • 救援人员端 rescuer:Tab 首页 / 事件 / 我的;工号以 1 开头
  • 仓管人员端 keeper:Tab 扫码 / 仓库 / 设备 / 消息 / 我;工号以 2 开头
  • 新增页面务必在 utils/role.utscanAccess 里登记角色归属

页面骨架用法

<hr-page page-bg="#F3F4F6" status-bg="#FFFFFF" nav-title="标题">
  <template v-slot:right>导航右侧</template>
  <view class="hr-body">内容</view>
  <template v-slot:footer>底部固定条</template>
  <template v-slot:tabbar><hr-tabbar :active="0"></hr-tabbar></template>
  <template v-slot:overlay>全屏/底部弹层</template>
</hr-page>
  • 三段式作业页(相机+列表+表单)用 :no-scroll="true"
  • 需要自定义头部的页面(伤员详情)直接手写根 view + hr-statusbar

数据来源

当前全部走 mock/index.uts;对接接口时替换 mock 取数实现,页面层不改。

伤情标记(2D 人体图鉴)

  • 数据层 utils/injury.uts:HrInjury 纯基础类型可直接 JSONinjuriesToJson/injuriesFromJson 存草稿与回显(接后端只换这两个)。
  • 部位 id 按视野命名(armVL=图左臂)、name 按伤员左右(正面 armVL→右上肢);9 个热区互不重叠。
  • 展示组件 components/hr-bodymap(正/背面,背面有脊柱线);业务/弹窗在 pages/triage/create.uvue(x-drawer 表单:部位自动填 + 其余默认填充 + 描述自动生成)。
  • 标记 = 编号圆点(不旋转,色=出血情况)+ 长轴条(长=尺寸、rotate=走向);改数组必须重建再赋值。
  • x-drawer 要点:v-model:showbottom 时 size=内容高;自定义 footer 需 :show-footer="true" + <template v-slot:footer>(自己关弹窗)。