1
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
# 医院救援 APP(hospital-rescue-app-v2)· 项目长期约定
|
||||
|
||||
## 技术栈
|
||||
- **uni-app x(uvue + uts)**,不是 uni-app(vue)。页面后缀 `.uvue`,脚本 `<script lang="uts" setup>`。
|
||||
- UI 组件库 **Tmui4(uni_modules/tmx-ui)**,全局配置在 `main.uts`(designSize 375 / unit px / 主色 #2563EB / dark light)。
|
||||
- easycom 自动扫描 `components/组件名/组件名.uvue`(本项目自研组件统一 `hr-` 前缀)。
|
||||
|
||||
## 设计规范
|
||||
- 设计稿基准 **375 × 812**,`mobile-design/v2/hospital-rescue-ui-v2.html` 为唯一视觉依据(35 屏)。
|
||||
- 设计稿 px = uvue px(uni-app x px 为逻辑像素),不要乘 2。
|
||||
- 主色 #2563EB;四色腕带分级:危重 #DC2626 / 重伤 #F59E0B / 轻伤 #10B981 / 死亡 #1F2937。
|
||||
- 底 #F3F4F6、卡片 #FFF、分割线 #F3F4F6 / #E5E7EB、文本 #1F2937 / #6B7280 / #9CA3AF。
|
||||
|
||||
## 角标(xBadge)几何 — 已定稿,别再改尺寸
|
||||
- 全站角标基准(用户指定):高/圆角 36rpx、字号 22.4rpx、红底白字;`.xBadge-countAndLabel` 用 `!important` 覆盖。
|
||||
- **角标实际宽 28px,不是 18px**:`min-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 6px(25%),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:grid`、`gap` → flex + margin
|
||||
4. **只使用** class 选择器(不写后代选择器);**复合选择器 `.a.b` 也不要用** → 选中态改用 JS 计算 style 字符串内联下发
|
||||
5. **Vue API 必须显式 import**:`import { 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.content` 是 `string|null|undefined` 要先判空
|
||||
10. `uni.showToast` 的 `icon` 是字面量联合类型('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`
|
||||
19. **UTS 里 `any` = Kotlin 非空 `Any`**,null 强转必崩:
|
||||
`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` 项不带 `request` 时 `el.getAny("request") as any` 抛 `null cannot be cast to non-null type kotlin.Any`;改为 `let requestdata : any | null = el.getAny("request")` + 去掉 `as any`(APP/MP 与 WEB 两个 return 分支都改) |
|
||||
|
||||
|
||||
## 分段切换(xRadioButton)— 全站 5 处已统一
|
||||
- 原「`hr-row` + `hr-chip` 横向滚动」筛选全部换成 `x-radio-button`:study/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.1px(89%)安全;改文案先跑 `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.uts` 的 `canAccess` 里登记角色归属
|
||||
|
||||
## 页面骨架用法
|
||||
```vue
|
||||
<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 纯基础类型可直接 JSON;`injuriesToJson/injuriesFromJson` 存草稿与回显(接后端只换这两个)。
|
||||
- 部位 id 按视野命名(armVL=图左臂)、name 按伤员左右(正面 armVL→右上肢);9 个热区互不重叠。
|
||||
- 展示组件 `components/hr-bodymap`(正/背面,背面有脊柱线);业务/弹窗在 `pages/triage/create.uvue`(x-drawer 表单:部位自动填 + 其余默认填充 + 描述自动生成)。
|
||||
- 标记 = 编号圆点(不旋转,色=出血情况)+ 长轴条(长=尺寸、rotate=走向);改数组必须重建再赋值。
|
||||
- x-drawer 要点:`v-model:show`;bottom 时 `size`=内容高;自定义 footer 需 `:show-footer="true"` + `<template v-slot:footer>`(自己关弹窗)。
|
||||
Reference in New Issue
Block a user