Compare commits
42 Commits
ae2be37923
...
6fafba87bb
| Author | SHA1 | Date |
|---|---|---|
|
|
6fafba87bb | 2 weeks ago |
|
|
497b53317b | 3 weeks ago |
|
|
b021f0949f | 4 weeks ago |
|
|
d2347bceec | 1 month ago |
|
|
65c3ea976e | 1 month ago |
|
|
f2ad084a8f | 1 month ago |
|
|
fcf04cf202 | 1 month ago |
|
|
5e454ce295 | 1 month ago |
|
|
847838f3c5 | 1 month ago |
|
|
6d53a8b081 | 1 month ago |
|
|
6aca21f0bf | 1 month ago |
|
|
f3af8dbb54 | 1 month ago |
|
|
b5f8e6ba78 | 2 months ago |
|
|
322c3c490e | 2 months ago |
|
|
383e1f4bba | 2 months ago |
|
|
c15418f1aa | 2 months ago |
|
|
d31fca2942 | 2 months ago |
|
|
452e65a86b | 3 months ago |
|
|
b2671cd939 | 3 months ago |
|
|
c3a47639d4 | 3 months ago |
|
|
de550aa169 | 3 months ago |
|
|
e949a70008 | 3 months ago |
|
|
0de235c1aa | 4 months ago |
|
|
6943ded348 | 4 months ago |
|
|
cbcf8c410b | 4 months ago |
|
|
b850a926c2 | 4 months ago |
|
|
fda038af27 | 4 months ago |
|
|
003646127f | 4 months ago |
|
|
5e89a8dda5 | 4 months ago |
|
|
d38a412543 | 4 months ago |
|
|
eb0f042730 | 5 months ago |
|
|
0824e8f811 | 5 months ago |
|
|
cb0edcb0af | 5 months ago |
|
|
2bb9399e64 | 6 months ago |
|
|
853c69aa7b | 6 months ago |
|
|
062a25c662 | 6 months ago |
|
|
2110fa4a50 | 6 months ago |
|
|
04625c2753 | 6 months ago |
|
|
a9e64f1ffe | 6 months ago |
|
|
f0ad8bde23 | 6 months ago |
|
|
e0354add61 | 6 months ago |
|
|
d2c122897d | 6 months ago |
451 changed files with 29210 additions and 20712 deletions
@ -1,4 +1,6 @@ |
|||||||
node_modules |
node_modules |
||||||
src/images/.svn |
src/images/.svn |
||||||
|
src/images/ |
||||||
.idea |
.idea |
||||||
.DS_Store |
.DS_Store |
||||||
|
/src/images |
||||||
|
|||||||
@ -0,0 +1,240 @@ |
|||||||
|
# 突眼日记接口联调计划 |
||||||
|
|
||||||
|
## 一、项目现状分析 |
||||||
|
|
||||||
|
### 1.1 已有页面结构 |
||||||
|
|
||||||
|
**医生端 (d\_ 前缀)** |
||||||
|
|
||||||
|
- `d_noteList` - 突眼记录列表页(拍摄示例页) |
||||||
|
|
||||||
|
- `d_noteDetail` - 突眼记录详情页 |
||||||
|
|
||||||
|
- `d_noteDiffData` - 凸眼度对比数据页(表格展示) |
||||||
|
|
||||||
|
_患者端 (patient/pages/note_)\* |
||||||
|
|
||||||
|
- `note` - 突眼日记首页 |
||||||
|
|
||||||
|
- `noteAdd` - 新增记录页 |
||||||
|
|
||||||
|
- `noteHistory` - 历史记录页 |
||||||
|
|
||||||
|
- `noteDiff` - 照片对比页 |
||||||
|
|
||||||
|
- `noteDiffEdit` - 对比编辑页 |
||||||
|
|
||||||
|
- `noteDemo` - 拍摄示例页 |
||||||
|
|
||||||
|
### 1.2 当前状态 |
||||||
|
|
||||||
|
- 所有页面都是静态数据,未接入接口 |
||||||
|
|
||||||
|
- 请求工具已封装在 `utils/request.ts` |
||||||
|
|
||||||
|
- 使用 `wx.ajax` 进行接口调用(在 app.ts 中挂载) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 二、需要的信息 |
||||||
|
|
||||||
|
在开始联调前,需要你提供以下信息: |
||||||
|
|
||||||
|
### 2.1 环境信息 |
||||||
|
|
||||||
|
| 信息项 | 说明 | 是否必需 | |
||||||
|
| -------------- | --------------------------- | -------- | |
||||||
|
| 后端环境地址 | 开发/测试服务器地址 | 是 | |
||||||
|
| 接口是否已部署 | 后端接口是否已上线可调用 | 是 | |
||||||
|
| 登录态获取方式 | 如何获取 loginState/session | 是 | |
||||||
|
|
||||||
|
### 2.2 接口测试信息 |
||||||
|
|
||||||
|
| 信息项 | 说明 | 是否必需 | |
||||||
|
| ---------- | -------------------------- | -------- | |
||||||
|
| 测试账号 | 医生账号和患者账号 | 是 | |
||||||
|
| 测试患者ID | 用于医生端接口测试 | 是 | |
||||||
|
| 已有数据 | 是否有已创建的突眼记录数据 | 否 | |
||||||
|
|
||||||
|
### 2.3 图片上传相关 |
||||||
|
|
||||||
|
| 信息项 | 说明 | 是否必需 | |
||||||
|
| ------------- | ----------------------------- | -------- | |
||||||
|
| 图片上传方式 | 上传到腾讯云COS还是自家服务器 | 是 | |
||||||
|
| 腾讯云IMS配置 | 是否已配置图片内容安全校验 | 否 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 三、接口联调清单 |
||||||
|
|
||||||
|
### 3.1 患者端接口 (10个) |
||||||
|
|
||||||
|
| 序号 | 接口 | 用途 | 对应页面 | 优先级 | |
||||||
|
| ---- | ------------------- | ---------------- | ----------------- | ------ | |
||||||
|
| 1 | `baseline-status` | 获取基准照状态 | note | P0 | |
||||||
|
| 2 | `record-list` | 获取记录列表 | note, noteHistory | P0 | |
||||||
|
| 3 | `record-detail` | 获取记录详情 | noteHistory | P0 | |
||||||
|
| 4 | `record-save` | 创建/更新记录 | noteAdd | P0 | |
||||||
|
| 5 | `photo-upload` | 上传照片 | noteAdd | P0 | |
||||||
|
| 6 | `record-delete` | 删除记录 | noteHistory | P1 | |
||||||
|
| 7 | `compare-dates` | 获取对比可用日期 | noteDiff | P1 | |
||||||
|
| 8 | `compare-photos` | 获取对比照片 | noteDiff | P1 | |
||||||
|
| 9 | `get-compare-angle` | 获取对比角度列表 | noteDiffEdit | P1 | |
||||||
|
| 10 | `get-session-id` | 获取唯一标识 | noteAdd | P2 | |
||||||
|
|
||||||
|
### 3.2 医生端接口 (6个) |
||||||
|
|
||||||
|
| 序号 | 接口 | 用途 | 对应页面 | 优先级 | |
||||||
|
| ---- | ------------------------------------ | ---------------- | --------------- | ------ | |
||||||
|
| 1 | `doctor/proptosis/latest` | 获取最近记录 | d_patientDetail | P0 | |
||||||
|
| 2 | `doctor/proptosis/list` | 获取患者记录列表 | d_noteList | P0 | |
||||||
|
| 3 | `doctor/proptosis/detail` | 获取记录详情 | d_noteDetail | P0 | |
||||||
|
| 4 | `doctor/proptosis/export` | Excel导出 | d_noteDiffData | P1 | |
||||||
|
| 5 | `doctor/proptosis/compare` | 获取对比数据 | d_noteDiffData | P1 | |
||||||
|
| 6 | `doctor/proptosis/get-compare-angle` | 获取对比角度 | d_noteDiffData | P1 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 四、实施步骤 |
||||||
|
|
||||||
|
### Phase 1: 基础联调准备 |
||||||
|
|
||||||
|
1. 确认后端环境可用 |
||||||
|
2. 获取测试账号和患者ID |
||||||
|
3. 验证登录态获取 |
||||||
|
|
||||||
|
### Phase 2: 患者端核心功能 |
||||||
|
|
||||||
|
1. 实现记录列表获取 (`record-list`) |
||||||
|
2. 实现记录详情获取 (`record-detail`) |
||||||
|
3. 实现记录创建/更新 (`record-save`) |
||||||
|
4. 实现照片上传 (`photo-upload`) |
||||||
|
|
||||||
|
### Phase 3: 患者端对比功能 |
||||||
|
|
||||||
|
1. 实现对比日期获取 (`compare-dates`) |
||||||
|
2. 实现对比照片获取 (`compare-photos`) |
||||||
|
3. 实现对比角度获取 (`get-compare-angle`) |
||||||
|
|
||||||
|
### Phase 4: 医生端功能 |
||||||
|
|
||||||
|
1. 实现患者最近记录获取 (`doctor/proptosis/latest`) |
||||||
|
2. 实现患者记录列表 (`doctor/proptosis/list`) |
||||||
|
3. 实现记录详情 (`doctor/proptosis/detail`) |
||||||
|
4. 实现对比数据获取 (`doctor/proptosis/compare`) |
||||||
|
|
||||||
|
### Phase 5: 导出功能 |
||||||
|
|
||||||
|
1. 实现Excel导出 (`doctor/proptosis/export`) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 五、数据结构映射 |
||||||
|
|
||||||
|
### 5.1 患者端 - 记录列表 |
||||||
|
|
||||||
|
```typescript |
||||||
|
// 接口返回 |
||||||
|
interface RecordItem { |
||||||
|
recordId: string |
||||||
|
recordDate: string |
||||||
|
isBaseline: number |
||||||
|
photoCount: number |
||||||
|
firstPhotoUrl: string |
||||||
|
treatmentCount: number |
||||||
|
leftEye: number |
||||||
|
rightEye: number |
||||||
|
interorbitalDistance: number |
||||||
|
uploadCompleted: number |
||||||
|
} |
||||||
|
|
||||||
|
// 页面使用 |
||||||
|
interface DataListItem { |
||||||
|
date: string // recordDate 格式化 |
||||||
|
left: string // leftEye |
||||||
|
spacing: string // interorbitalDistance |
||||||
|
right: string // rightEye |
||||||
|
count: string // treatmentCount |
||||||
|
isBaseline: boolean |
||||||
|
photoCount: number |
||||||
|
firstPhotoUrl: string |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### 5.2 医生端 - 凸眼度对比表格 |
||||||
|
|
||||||
|
```typescript |
||||||
|
// 接口返回 (compare 接口) |
||||||
|
interface CompareItem { |
||||||
|
recordId: string |
||||||
|
recordDate: string |
||||||
|
photoUrl: string |
||||||
|
leftEye: number |
||||||
|
rightEye: number |
||||||
|
interorbitalDistance: number |
||||||
|
treatmentCount: number |
||||||
|
} |
||||||
|
|
||||||
|
// 页面使用 (d_noteDiffData) |
||||||
|
interface TableItem { |
||||||
|
date: string // recordDate 格式化 2026.1.5 |
||||||
|
left: string // leftEye |
||||||
|
spacing: string // interorbitalDistance |
||||||
|
right: string // rightEye |
||||||
|
count: string // treatmentCount |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 六、注意事项 |
||||||
|
|
||||||
|
### 6.1 业务规则 |
||||||
|
|
||||||
|
1. 同一天只能创建一条记录 |
||||||
|
2. 必须先上传照片再保存记录(photoIds 关联) |
||||||
|
3. 基准照只能有一张 |
||||||
|
4. 图片需要经过腾讯云IMS安全校验 |
||||||
|
|
||||||
|
### 6.2 技术注意 |
||||||
|
|
||||||
|
1. 使用 `wx.ajax` 进行请求(已挂载在 app 上) |
||||||
|
2. 医生端页面需要等待登录态 `app.waitLogin({ type: [2] })` |
||||||
|
3. 患者端页面需要等待登录态 `app.waitLogin({ type: [1] })` |
||||||
|
4. 图片URL需要拼接 `{{imageUrl}}` 前缀 |
||||||
|
|
||||||
|
### 6.3 角度映射 |
||||||
|
|
||||||
|
接口角度值与页面显示名称映射: |
||||||
|
|
||||||
|
```typescript |
||||||
|
const angleMap = { |
||||||
|
front_open: '正面睁眼', |
||||||
|
front_closed: '正面闭眼', |
||||||
|
front_looking_up: '正面仰头', |
||||||
|
side_left_90: '90°左侧', |
||||||
|
side_right_90: '90°右侧', |
||||||
|
side_left_45: '45°左侧', |
||||||
|
side_right_45: '45°右侧', |
||||||
|
eye_up_left: '左上', |
||||||
|
eye_up: '向上', |
||||||
|
eye_up_right: '右上', |
||||||
|
eye_left: '向左', |
||||||
|
eye_right: '向右', |
||||||
|
eye_down_left: '左下', |
||||||
|
eye_down: '向下', |
||||||
|
eye_down_right: '右下', |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 七、下一步 |
||||||
|
|
||||||
|
请提供以下信息,我将开始实施联调: |
||||||
|
|
||||||
|
1. **后端环境地址**(如:<https://m.xd.hbraas.com)> |
||||||
|
2. **测试账号**(医生和患者各一个) |
||||||
|
3. **测试患者ID**(用于医生端接口测试) |
||||||
|
4. **接口是否已部署**(确认后端已完成开发) |
||||||
|
5. **图片上传方式**(腾讯云COS或自家服务器) |
||||||
@ -0,0 +1,419 @@ |
|||||||
|
# 项目规则 - 信达小程序 (Xinda Mini Program) |
||||||
|
|
||||||
|
WeChat Mini Program for thyroid eye disease patient management. Dual-mode app serving both patients (患者端) and doctors (医生端). |
||||||
|
|
||||||
|
## Project Type |
||||||
|
|
||||||
|
- **Framework**: WeChat Mini Program v3.7.7 |
||||||
|
- **Language**: TypeScript (strict mode) |
||||||
|
- **Styling**: SCSS/Sass via `useCompilerPlugins` |
||||||
|
- **UI Library**: Vant Weapp (@vant/weapp) |
||||||
|
- **Renderer**: Skyline enabled |
||||||
|
- **App ID**: `wxf9ce8010f1ad24aa` (dev), `wx71ac9c27c3c3e3f4` (prod) |
||||||
|
|
||||||
|
## Directory Structure |
||||||
|
|
||||||
|
``` |
||||||
|
src/ |
||||||
|
├── pages/ # Doctor-side main pages (医生端) |
||||||
|
├── patient/pages/ # Patient-side pages (subpackage) |
||||||
|
├── gift/pages/ # DTP pharmacy pages (subpackage) |
||||||
|
├── doc/pages/ # Privacy/terms documentation (subpackage) |
||||||
|
├── components/ # Shared components |
||||||
|
├── utils/ # Request, page/component wrappers, utilities |
||||||
|
├── app.ts # App entry with global data |
||||||
|
└── config.ts # Environment configs per appId |
||||||
|
``` |
||||||
|
|
||||||
|
## Key Conventions |
||||||
|
|
||||||
|
### Path Aliases |
||||||
|
|
||||||
|
- `@/*` → `src/*` (configured in tsconfig.json and app.json `resolveAlias`) |
||||||
|
- Use `@/utils/request` not relative paths |
||||||
|
|
||||||
|
### User Types & Routing |
||||||
|
|
||||||
|
Login types enforced in `app.ts`: |
||||||
|
|
||||||
|
- `0`: Not logged in |
||||||
|
- `1`: Patient (患者) → routes to `/patient/pages/*` |
||||||
|
- `2`: Doctor (医生) → routes to `/pages/*` |
||||||
|
|
||||||
|
Use `app.zdWaitLogin()` to guard pages that require login. |
||||||
|
|
||||||
|
### Page/Component Wrappers |
||||||
|
|
||||||
|
`app.ts` overrides global `Page` and `Component` with wrappers from `utils/page.ts` and `utils/component.ts`: |
||||||
|
|
||||||
|
- Auto-sets `imageUrl` and `Timestamp` on page load |
||||||
|
- Auto-handles navbar background on scroll |
||||||
|
- Provides default share behavior |
||||||
|
|
||||||
|
### Modal Colors (Required) |
||||||
|
|
||||||
|
All `wx.showModal` must use: |
||||||
|
|
||||||
|
```ts |
||||||
|
wx.showModal({ |
||||||
|
confirmColor: '#8c75d0', |
||||||
|
cancelColor: '#141515', |
||||||
|
}) |
||||||
|
``` |
||||||
|
|
||||||
|
### App Instance (Required) |
||||||
|
|
||||||
|
`getApp()` should be called at the top of the file, not inside methods: |
||||||
|
|
||||||
|
```ts |
||||||
|
// ✅ Correct |
||||||
|
const app = getApp<IAppOption>() |
||||||
|
|
||||||
|
Page({ |
||||||
|
onLoad() { |
||||||
|
// Use app directly |
||||||
|
console.log(app.globalData) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
// ❌ Incorrect |
||||||
|
Page({ |
||||||
|
onLoad() { |
||||||
|
const app = getApp<IAppOption>() |
||||||
|
console.log(app.globalData) |
||||||
|
} |
||||||
|
}) |
||||||
|
``` |
||||||
|
|
||||||
|
## WXML 开发规范 |
||||||
|
|
||||||
|
### 1. 不支持在 WXML 中直接调用方法 |
||||||
|
|
||||||
|
**错误示例:** |
||||||
|
|
||||||
|
```xml |
||||||
|
<!-- ❌ 错误:WXML 中不能直接调用方法 --> |
||||||
|
<view>{{getUploadedCount()}}</view> |
||||||
|
<view wx:if="{{hasPhotos()}}">内容</view> |
||||||
|
``` |
||||||
|
|
||||||
|
**正确做法:** |
||||||
|
|
||||||
|
```xml |
||||||
|
<!-- ✅ 正确:使用数据绑定 --> |
||||||
|
<view>{{uploadedCount}}</view> |
||||||
|
<view wx:if="{{hasPhotos}}">内容</view> |
||||||
|
``` |
||||||
|
|
||||||
|
```typescript |
||||||
|
// 在 TS 中计算好数据 |
||||||
|
this.setData({ |
||||||
|
uploadedCount: photos.length, |
||||||
|
hasPhotos: photos.length > 0 |
||||||
|
}) |
||||||
|
``` |
||||||
|
|
||||||
|
### 2. 列表渲染时使用 wx:key |
||||||
|
|
||||||
|
```xml |
||||||
|
<view wx:for="{{list}}" wx:key="id">{{item.name}}</view> |
||||||
|
``` |
||||||
|
|
||||||
|
### 3. 条件渲染 |
||||||
|
|
||||||
|
```xml |
||||||
|
<view wx:if="{{condition}}">显示</view> |
||||||
|
<view wx:else>隐藏</view> |
||||||
|
``` |
||||||
|
|
||||||
|
### 4. WXML 表达式限制 |
||||||
|
|
||||||
|
WXML 中的双大括号 `{{}}` 只能使用简单的表达式,**不支持**以下语法: |
||||||
|
|
||||||
|
**❌ 不支持的语法:** |
||||||
|
|
||||||
|
```xml |
||||||
|
<!-- 方法调用 --> |
||||||
|
<view>{{arr.indexOf(item) > -1}}</view> |
||||||
|
|
||||||
|
<!-- 复杂表达式 --> |
||||||
|
<view>{{obj.method().property}}</view> |
||||||
|
|
||||||
|
<!-- 正则表达式 --> |
||||||
|
<view>{{str.match(/regex/)}}</view> |
||||||
|
|
||||||
|
<!-- new 操作符 --> |
||||||
|
<view>{{new Date().getTime()}}</view> |
||||||
|
``` |
||||||
|
|
||||||
|
**✅ 支持的语法:** |
||||||
|
|
||||||
|
```xml |
||||||
|
<!-- 简单属性访问 --> |
||||||
|
<view>{{item.name}}</view> |
||||||
|
|
||||||
|
<!-- 三元表达式 --> |
||||||
|
<view class="{{item.isActive ? 'active' : ''}}">内容</view> |
||||||
|
|
||||||
|
<!-- 简单比较 --> |
||||||
|
<view wx:if="{{count > 0}}">有数据</view> |
||||||
|
|
||||||
|
<!-- 逻辑运算 --> |
||||||
|
<view wx:if="{{isLogin && isVip}}">VIP用户</view> |
||||||
|
``` |
||||||
|
|
||||||
|
**解决方案:** |
||||||
|
|
||||||
|
```typescript |
||||||
|
// 在 TS 中预处理数据,将复杂计算转换为简单布尔值 |
||||||
|
this.setData({ |
||||||
|
// ❌ 不要在 WXML 中使用 indexOf |
||||||
|
// isSelected: selectedDates.indexOf(item.recordId) > -1 |
||||||
|
|
||||||
|
// ✅ 在数据中直接提供 isSelected 字段 |
||||||
|
list: rawList.map(item => ({ |
||||||
|
...item, |
||||||
|
isSelected: selectedDates.includes(item.recordId) |
||||||
|
})) |
||||||
|
}) |
||||||
|
``` |
||||||
|
|
||||||
|
## Available Commands |
||||||
|
|
||||||
|
```bash |
||||||
|
# Lint and auto-fix (only command available) |
||||||
|
npm run lint:fix |
||||||
|
|
||||||
|
# Install dependencies (pnpm preferred based on lockfile) |
||||||
|
pnpm install |
||||||
|
|
||||||
|
# Build: Use WeChat Developer Tools |
||||||
|
# - Import project with src/ as root |
||||||
|
# - project.config.json at repo root |
||||||
|
``` |
||||||
|
|
||||||
|
## NPM Dependencies |
||||||
|
|
||||||
|
**Critical**: After `npm install`, run **Tools → Build npm** in WeChat Developer Tools to generate `miniprogram_npm/`. This is required for Vant and other packages. |
||||||
|
|
||||||
|
Key dependencies: |
||||||
|
|
||||||
|
- `@vant/weapp`: UI components |
||||||
|
- `miniprogram-licia`: Utility library (available as `licia`) |
||||||
|
- `dayjs`: Date handling |
||||||
|
- `mp-html`: Rich HTML rendering |
||||||
|
|
||||||
|
## Images & Assets |
||||||
|
|
||||||
|
**Images are stored in SVN, not git.** |
||||||
|
|
||||||
|
- SVN URL: `svn://39.106.86.127:28386/projects/xd/proj_src/shop/frontend/web/xd/` |
||||||
|
- Local path: `src/images/` (gitignored) |
||||||
|
- Excluded from upload via `project.config.json` packOptions |
||||||
|
- Only `/images/tabbar/*` is included in uploads |
||||||
|
|
||||||
|
Image URL pattern: |
||||||
|
|
||||||
|
``` |
||||||
|
{{imageUrl}}/path/to/image.png?t={{Timestamp}} |
||||||
|
``` |
||||||
|
|
||||||
|
## TypeScript Configuration |
||||||
|
|
||||||
|
- Strict mode enabled |
||||||
|
- `noImplicitAny: false` (allows implicit any) |
||||||
|
- Paths: `@/*` mapped to `src/*` |
||||||
|
- Types: `miniprogram-api-typings` for WX API |
||||||
|
|
||||||
|
Global types in `typings/index.d.ts`: |
||||||
|
|
||||||
|
- `IAppOption`: Global app instance interface |
||||||
|
- `pageType`: 0 | 1 | 2 for user types |
||||||
|
- `wx.ajax`: Extended request method |
||||||
|
|
||||||
|
## ESLint & Formatting |
||||||
|
|
||||||
|
- Config: `@antfu/eslint-config` (flat config in `eslint.config.js`) |
||||||
|
- Prettier: 2-space tabs, no semis, single quotes, trailing commas |
||||||
|
- WXML files parsed as HTML, WXSS as CSS |
||||||
|
- Globals defined: `wx`, `App`, `Page`, `Component`, `getCurrentPages`, etc. |
||||||
|
|
||||||
|
## Environment Configuration |
||||||
|
|
||||||
|
Selected by App ID in `src/config.ts`: |
||||||
|
|
||||||
|
- `wxf9ce8010f1ad24aa`: Dev/Staging (hbraas.com) |
||||||
|
- `wx71ac9c27c3c3e3f4`: Production (hbsaas.com) |
||||||
|
|
||||||
|
App reads `wx.getAccountInfoSync().miniProgram.appId` on launch to select config. |
||||||
|
|
||||||
|
## Testing & Development |
||||||
|
|
||||||
|
- No unit test framework configured |
||||||
|
- Manual testing via WeChat Developer Tools |
||||||
|
- `project.private.config.json` has hot reload enabled (`compileHotReLoad: true`) |
||||||
|
- Predefined test pages in `project.private.config.json` condition list |
||||||
|
|
||||||
|
## Common Gotchas |
||||||
|
|
||||||
|
1. **NPM packages**: Must run "Build npm" in WeChat tools after install |
||||||
|
2. **Images**: Will 404 if SVN images not checked out to `src/images/` |
||||||
|
3. **Subpackages**: Patient pages are in `patient/` subpackage, not main package |
||||||
|
4. **Skyline**: Enabled in rendererOptions, some components may behave differently |
||||||
|
5. **Login flow**: App automatically calls `startLogin()` on launch; pages must wait via `app.zdWaitLogin()` |
||||||
|
|
||||||
|
## 自定义导航栏规范 |
||||||
|
|
||||||
|
### 统一使用 navbar 组件 |
||||||
|
|
||||||
|
所有需要自定义导航的页面统一使用 `/components/navbar/index`。`/components/zd-navBar/navBar` 是历史遗留组件,新页面**不要使用**。 |
||||||
|
|
||||||
|
**JSON 配置:** |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"navigationStyle": "custom", |
||||||
|
"usingComponents": { |
||||||
|
"navbar": "/components/navbar/index" |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**WXML 用法:** |
||||||
|
|
||||||
|
```xml |
||||||
|
<navbar fixed title="页面标题" custom-style="background:{{background}}"> |
||||||
|
<van-icon name="arrow-left" slot="left" size="18px" color="#000" bind:tap="handleBack" /> |
||||||
|
</navbar> |
||||||
|
|
||||||
|
<view class="page" style="padding-top:{{pageTop+20}}px;"> |
||||||
|
``` |
||||||
|
|
||||||
|
**TS 实现:** |
||||||
|
|
||||||
|
```typescript |
||||||
|
handleBack() { |
||||||
|
wx.navigateBack() |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### 关键参数 |
||||||
|
|
||||||
|
| 属性 | 说明 | 常用值 | |
||||||
|
| -------------- | ------------ | --------------------------- | |
||||||
|
| `fixed` | 固定导航栏 | `fixed` | |
||||||
|
| `title` | 导航栏标题 | 字符串 | |
||||||
|
| `custom-style` | 自定义样式 | `background:{{background}}` | |
||||||
|
| `leftArrow` | 显示返回箭头 | Boolean | |
||||||
|
| `slot="left"` | 左侧插槽 | van-icon 返回按钮 | |
||||||
|
| `bind:tap` | 返回按钮点击 | `handleBack` | |
||||||
|
|
||||||
|
### pageTop 变量 |
||||||
|
|
||||||
|
`pageTop` 由 `utils/page.ts` wrapper 自动注入,表示导航栏高度。页面内容区使用 `padding-top:{{pageTop+20}}px;` 避开导航栏。**不要手动计算 navBarHeight**。 |
||||||
|
|
||||||
|
## 返回拦截与弹窗规范 |
||||||
|
|
||||||
|
### 使用自定义弹窗替代系统弹窗 |
||||||
|
|
||||||
|
**禁止使用** `wx.showModal` 进行操作确认,统一使用项目内的 popup 组件: |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"usingComponents": { |
||||||
|
"popup": "/components/popup/index" |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
```xml |
||||||
|
<popup show="{{popupShow}}" type="{{popupType}}" params="{{popupParams}}" bind:ok="handlePopupOk" bind:cancel="handlePopupCancel" /> |
||||||
|
``` |
||||||
|
|
||||||
|
### 常用 popup 类型 |
||||||
|
|
||||||
|
| 类型 | 场景 | 标题 | 确认按钮 | 取消按钮 | |
||||||
|
| --------- | -------------- | ------------------ | -------- | -------- | |
||||||
|
| `popup15` | 删除确认 | "确认删除记录?" | 确认删除 | 取消 | |
||||||
|
| `popup16` | 未保存数据退出 | "您的记录还未保存" | 保存记录 | 退出 | |
||||||
|
| `popup17` | 裁剪未保存退出 | "您有裁剪的照片" | 退出 | 取消 | |
||||||
|
|
||||||
|
### popup 数据定义 |
||||||
|
|
||||||
|
```typescript |
||||||
|
data: { |
||||||
|
popupShow: false, |
||||||
|
popupType: 'popup16', |
||||||
|
popupParams: { |
||||||
|
close: false, |
||||||
|
position: 'center', |
||||||
|
} as any, |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### 返回拦截模式 |
||||||
|
|
||||||
|
当页面有未保存数据时,使用自定义导航 + `bind:back` 拦截返回: |
||||||
|
|
||||||
|
```typescript |
||||||
|
handleBack() { |
||||||
|
if (this.hasUnsavedData()) { |
||||||
|
this.setData({ popupShow: true, popupType: 'popup16' }) |
||||||
|
} else { |
||||||
|
wx.navigateBack() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
handlePopupOk() { |
||||||
|
this.setData({ popupShow: false }) |
||||||
|
this.handleSave() |
||||||
|
} |
||||||
|
|
||||||
|
handlePopupCancel() { |
||||||
|
this.setData({ popupShow: false }) |
||||||
|
wx.navigateBack() |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
**注意**:`navigationStyle: "custom"` 后系统返回手势无法拦截,只有 navBar 的返回按钮可被拦截。如需拦截系统返回手势,需要额外使用 `wx.enableAlertBeforeUnload`。 |
||||||
|
|
||||||
|
## wx.cropImage 规范 |
||||||
|
|
||||||
|
`wx.cropImage` 的 `src` 参数**只接受本地文件路径**,不支持网络 URL。裁剪网络图片时必须先用 `wx.getImageInfo` 下载到本地: |
||||||
|
|
||||||
|
```typescript |
||||||
|
handleCrop(e: any) { |
||||||
|
const index = e.currentTarget.dataset.index |
||||||
|
const photo = this.data.photos[index] |
||||||
|
|
||||||
|
wx.showLoading({ title: '加载中...' }) |
||||||
|
wx.getImageInfo({ |
||||||
|
src: photo.photoUrl, |
||||||
|
success: (imgRes) => { |
||||||
|
wx.hideLoading() |
||||||
|
wx.cropImage({ |
||||||
|
src: imgRes.path, |
||||||
|
cropScale: '9:16', |
||||||
|
success: (cropRes) => { |
||||||
|
photos[index].croppedUrl = cropRes.tempFilePath |
||||||
|
this.setData({ photos }) |
||||||
|
}, |
||||||
|
fail: (err) => { |
||||||
|
if (err.errMsg?.includes('cancel')) return |
||||||
|
wx.showToast({ title: '裁剪取消', icon: 'none' }) |
||||||
|
}, |
||||||
|
}) |
||||||
|
}, |
||||||
|
fail: () => { |
||||||
|
wx.hideLoading() |
||||||
|
wx.showToast({ title: '图片加载失败', icon: 'none' }) |
||||||
|
}, |
||||||
|
}) |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 子包组件引用规范 |
||||||
|
|
||||||
|
主包页面(`src/pages/`)**不能引用**子包组件(`src/patient/components/`)。如果主包和子包都需要使用某个组件,需要将组件复制到 `src/components/` 目录下。 |
||||||
|
|
||||||
|
示例:`image-merge` 组件在 `patient/components/` 和 `components/` 各有一份,分别供子包和主包页面使用。 |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
// Folder-specific settings |
||||||
|
// |
||||||
|
// For a full list of overridable settings, and general information on folder-specific settings, |
||||||
|
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files |
||||||
|
{ |
||||||
|
"lsp": { |
||||||
|
"emmet-language-server": { |
||||||
|
"initialization_options": { |
||||||
|
"preferences": { |
||||||
|
"css.intUnit": "rpx", |
||||||
|
"css.floatUnitr": "rpx", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
@ -1 +1 @@ |
|||||||
export {}; |
export {} |
||||||
|
|||||||
@ -1 +1 @@ |
|||||||
export {}; |
export {} |
||||||
|
|||||||
@ -1 +1 @@ |
|||||||
export {}; |
export {} |
||||||
|
|||||||
@ -1 +1 @@ |
|||||||
export {}; |
export {} |
||||||
|
|||||||
@ -1 +1 @@ |
|||||||
export {}; |
export {} |
||||||
|
|||||||
@ -1 +1 @@ |
|||||||
export {}; |
export {} |
||||||
|
|||||||
@ -1,23 +1,25 @@ |
|||||||
export default class Logger { |
export default class Logger { |
||||||
info(msg) { |
info(msg) { |
||||||
console.log( |
console.log( |
||||||
'%cInfo: %c' + msg, |
`%cInfo: %c${msg}`, |
||||||
'color:#FF0080;font-weight:bold', |
'color:#FF0080;font-weight:bold', |
||||||
'color: #FF509B' |
'color: #FF509B', |
||||||
) |
) |
||||||
} |
} |
||||||
|
|
||||||
warn(msg) { |
warn(msg) { |
||||||
console.log( |
console.log( |
||||||
'%cWarn: %c' + msg, |
`%cWarn: %c${msg}`, |
||||||
'color:#FF6600;font-weight:bold', |
'color:#FF6600;font-weight:bold', |
||||||
'color: #FF9933' |
'color: #FF9933', |
||||||
) |
) |
||||||
} |
} |
||||||
|
|
||||||
tips(msg) { |
tips(msg) { |
||||||
console.log( |
console.log( |
||||||
'%cTips: %c' + msg, |
`%cTips: %c${msg}`, |
||||||
'color:#00B200;font-weight:bold', |
'color:#00B200;font-weight:bold', |
||||||
'color: #00CC33' |
'color: #00CC33', |
||||||
) |
) |
||||||
} |
} |
||||||
} |
} |
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"component": true, |
||||||
|
"usingComponents": { |
||||||
|
"van-button": "@vant/weapp/button/index", |
||||||
|
"van-icon": "@vant/weapp/icon/index", |
||||||
|
"van-toast": "@vant/weapp/toast/index" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
.merge-canvas { |
||||||
|
position: fixed; |
||||||
|
left: -9999px; |
||||||
|
top: -9999px; |
||||||
|
width: 1px; |
||||||
|
height: 1px; |
||||||
|
} |
||||||
@ -0,0 +1,200 @@ |
|||||||
|
interface ImageItem { |
||||||
|
src: string |
||||||
|
time?: string |
||||||
|
} |
||||||
|
|
||||||
|
Component({ |
||||||
|
properties: { |
||||||
|
id: { |
||||||
|
type: String, |
||||||
|
value: 'default', |
||||||
|
}, |
||||||
|
}, |
||||||
|
|
||||||
|
data: { |
||||||
|
imageList: [] as ImageItem[], |
||||||
|
mergedImage: '', |
||||||
|
isLoading: false, |
||||||
|
}, |
||||||
|
|
||||||
|
methods: { |
||||||
|
mergeImages(imageList: ImageItem[]) { |
||||||
|
if (this.data.isLoading) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (!imageList || imageList.length < 2) { |
||||||
|
wx.showToast({ title: '至少需要2张图片才能拼接', icon: 'none' }) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
wx.showLoading({ title: '拼接中...', mask: true }) |
||||||
|
this.setData({ isLoading: true, imageList }) |
||||||
|
|
||||||
|
this.validateImages(imageList) |
||||||
|
.then(() => this.drawImagesOnCanvas()) |
||||||
|
.then((mergedImage) => { |
||||||
|
wx.hideLoading() |
||||||
|
this.setData({ |
||||||
|
mergedImage, |
||||||
|
isLoading: false, |
||||||
|
}) |
||||||
|
this.triggerEvent('save', { tempFilePath: mergedImage }) |
||||||
|
wx.previewImage({ |
||||||
|
urls: [mergedImage], |
||||||
|
current: mergedImage, |
||||||
|
showmenu: true, |
||||||
|
}) |
||||||
|
}) |
||||||
|
.catch((error) => { |
||||||
|
wx.hideLoading() |
||||||
|
console.error('拼接失败:', error) |
||||||
|
this.setData({ isLoading: false }) |
||||||
|
wx.showToast({ title: error.message || '拼接失败,请重试', icon: 'none' }) |
||||||
|
this.triggerEvent('error', { reason: error.message }) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
validateImages(imageList: ImageItem[]): Promise<void> { |
||||||
|
return Promise.all( |
||||||
|
imageList.map( |
||||||
|
(item, index) => |
||||||
|
new Promise<void>((resolve, reject) => { |
||||||
|
wx.getImageInfo({ |
||||||
|
src: item.src, |
||||||
|
success: () => resolve(), |
||||||
|
fail: () => reject(new Error(`第${index + 1}张图片加载失败`)), |
||||||
|
}) |
||||||
|
}), |
||||||
|
), |
||||||
|
) |
||||||
|
}, |
||||||
|
|
||||||
|
formatTime(date: Date): string { |
||||||
|
const year = date.getFullYear() |
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0') |
||||||
|
const day = String(date.getDate()).padStart(2, '0') |
||||||
|
const hours = String(date.getHours()).padStart(2, '0') |
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0') |
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}` |
||||||
|
}, |
||||||
|
|
||||||
|
drawImagesOnCanvas(): Promise<string> { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
const { imageList, id } = this.data |
||||||
|
const query = this.createSelectorQuery() |
||||||
|
query |
||||||
|
.in(this) |
||||||
|
.select(`#mergeCanvas-${id}`) |
||||||
|
.fields({ node: true, size: true }) |
||||||
|
.exec((res) => { |
||||||
|
if (!res[0]) { |
||||||
|
reject(new Error('Canvas not found')) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const canvas = res[0].node |
||||||
|
const ctx = canvas.getContext('2d') |
||||||
|
|
||||||
|
Promise.all(imageList.map(item => this.getImageInfo(item.src))) |
||||||
|
.then((imageInfos) => { |
||||||
|
const targetWidth = 750 |
||||||
|
const pixelRatio = wx.getWindowInfo().pixelRatio |
||||||
|
const canvasWidth = Math.floor((targetWidth * pixelRatio) / 2) |
||||||
|
const fontScale = pixelRatio / 2 |
||||||
|
|
||||||
|
let totalHeight = 0 |
||||||
|
const scaledHeights: number[] = [] |
||||||
|
|
||||||
|
imageInfos.forEach((info) => { |
||||||
|
const scale = canvasWidth / info.width |
||||||
|
const scaledHeight = Math.floor(info.height * scale) |
||||||
|
scaledHeights.push(scaledHeight) |
||||||
|
totalHeight += scaledHeight |
||||||
|
}) |
||||||
|
|
||||||
|
canvas.width = canvasWidth |
||||||
|
canvas.height = totalHeight |
||||||
|
|
||||||
|
let currentY = 0 |
||||||
|
let loadedCount = 0 |
||||||
|
|
||||||
|
// 预计算每张图片的 Y 偏移,确保顺序正确
|
||||||
|
const yPositions: number[] = [] |
||||||
|
scaledHeights.forEach((h, i) => { |
||||||
|
yPositions.push(currentY) |
||||||
|
currentY += h |
||||||
|
}) |
||||||
|
|
||||||
|
loadedCount = 0 |
||||||
|
currentY = 0 |
||||||
|
|
||||||
|
imageInfos.forEach((info, index) => { |
||||||
|
const img = canvas.createImage() |
||||||
|
img.src = info.path |
||||||
|
img.onload = () => { |
||||||
|
ctx.drawImage(img, 0, yPositions[index], canvasWidth, scaledHeights[index]) |
||||||
|
|
||||||
|
const timeText = imageList[index].time || this.formatTime(new Date()) |
||||||
|
const padding = 20 * fontScale |
||||||
|
const fontSize = Math.round(28 * fontScale) |
||||||
|
const textY = yPositions[index] + 40 * fontScale |
||||||
|
|
||||||
|
ctx.font = `bold ${fontSize}px sans-serif` |
||||||
|
ctx.textBaseline = 'middle' |
||||||
|
const textMetrics = ctx.measureText(timeText) |
||||||
|
const bgPaddingH = 16 * fontScale |
||||||
|
const bgPaddingV = 10 * fontScale |
||||||
|
const bgH = fontSize + bgPaddingV * 2 |
||||||
|
const bgX = padding - bgPaddingH |
||||||
|
const bgY = textY - bgH / 2 |
||||||
|
const bgW = textMetrics.width + bgPaddingH * 2 |
||||||
|
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.45)' |
||||||
|
ctx.fillRect(bgX, bgY, bgW, bgH) |
||||||
|
|
||||||
|
ctx.fillStyle = '#ffffff' |
||||||
|
ctx.textAlign = 'left' |
||||||
|
ctx.fillText(timeText, padding, textY) |
||||||
|
|
||||||
|
if (index === imageInfos.length - 1) { |
||||||
|
const lastImageBottom = yPositions[index] + scaledHeights[index] |
||||||
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)' |
||||||
|
ctx.font = `${Math.round(24 * fontScale)}px sans-serif` |
||||||
|
ctx.textAlign = 'right' |
||||||
|
ctx.fillText('由-TED关爱小助手-小程序生成', canvasWidth - 20 * fontScale, lastImageBottom - 20 * fontScale) |
||||||
|
} |
||||||
|
|
||||||
|
loadedCount++ |
||||||
|
|
||||||
|
if (loadedCount === imageInfos.length) { |
||||||
|
wx.canvasToTempFilePath({ |
||||||
|
canvas, |
||||||
|
success: (result) => { |
||||||
|
resolve(result.tempFilePath) |
||||||
|
}, |
||||||
|
fail: reject, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
img.onerror = () => { |
||||||
|
reject(new Error(`Failed to load image: ${info.path}`)) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
.catch(reject) |
||||||
|
}) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
getImageInfo(src: string): Promise<WechatMiniprogram.GetImageInfoSuccessCallbackResult> { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
wx.getImageInfo({ |
||||||
|
src, |
||||||
|
success: resolve, |
||||||
|
fail: reject, |
||||||
|
}) |
||||||
|
}) |
||||||
|
}, |
||||||
|
}, |
||||||
|
}) |
||||||
@ -0,0 +1 @@ |
|||||||
|
<canvas type="2d" id="mergeCanvas-{{id}}" class="merge-canvas"></canvas> |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"component": true, |
||||||
|
"usingComponents": { |
||||||
|
"van-icon": "@vant/weapp/icon/index", |
||||||
|
"navbar": "/components/navbar/index" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,157 @@ |
|||||||
|
.preview-container { |
||||||
|
position: fixed; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
bottom: 0; |
||||||
|
background: #000; |
||||||
|
z-index: 1000; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
opacity: 0; |
||||||
|
visibility: hidden; |
||||||
|
transition: |
||||||
|
opacity 0.3s, |
||||||
|
visibility 0.3s; |
||||||
|
|
||||||
|
&.show { |
||||||
|
opacity: 1; |
||||||
|
visibility: visible; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 图片展示区域 |
||||||
|
.image-wrapper { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
overflow: hidden; |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.order { |
||||||
|
position: absolute; |
||||||
|
left: 50%; |
||||||
|
transform: translate(-50%, 0 ); |
||||||
|
padding: 18rpx 32rpx; |
||||||
|
font-size: 40rpx; |
||||||
|
font-weight: bold; |
||||||
|
text-align: center; |
||||||
|
color: #211d2e; |
||||||
|
border-radius: 94rpx; |
||||||
|
display: inline-flex; |
||||||
|
align-items: baseline; |
||||||
|
background-color: #fff; |
||||||
|
z-index: 10; |
||||||
|
white-space: nowrap; |
||||||
|
|
||||||
|
.num { |
||||||
|
margin-left: 20rpx; |
||||||
|
} |
||||||
|
|
||||||
|
.m-num { |
||||||
|
font-size: 28rpx; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.preview-image { |
||||||
|
display: block; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
// 左右切换按钮 |
||||||
|
.nav-btn { |
||||||
|
position: absolute; |
||||||
|
top: 50%; |
||||||
|
transform: translateY(-50%); |
||||||
|
width: 72rpx; |
||||||
|
height: 72rpx; |
||||||
|
border-radius: 50%; |
||||||
|
background: rgba(0, 0, 0, 0.4); |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
z-index: 10; |
||||||
|
|
||||||
|
&:active:not(.disabled) { |
||||||
|
background: rgba(0, 0, 0, 0.6); |
||||||
|
} |
||||||
|
|
||||||
|
&.disabled { |
||||||
|
opacity: 0.3; |
||||||
|
} |
||||||
|
|
||||||
|
&.nav-prev { |
||||||
|
left: 24rpx; |
||||||
|
} |
||||||
|
|
||||||
|
&.nav-next { |
||||||
|
right: 24rpx; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 页码指示器 |
||||||
|
.indicator { |
||||||
|
position: absolute; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
text-align: center; |
||||||
|
color: #fff; |
||||||
|
font-size: 28rpx; |
||||||
|
z-index: 10; |
||||||
|
bottom: calc(168rpx + env(safe-area-inset-bottom)); |
||||||
|
|
||||||
|
.current { |
||||||
|
font-weight: 600; |
||||||
|
} |
||||||
|
|
||||||
|
.separator { |
||||||
|
margin: 0 8rpx; |
||||||
|
opacity: 0.6; |
||||||
|
} |
||||||
|
|
||||||
|
.total { |
||||||
|
opacity: 0.6; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 底部操作栏 |
||||||
|
.action-bar { |
||||||
|
position: absolute; |
||||||
|
bottom: 0; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
padding: 40rpx; |
||||||
|
padding-bottom: calc(40rpx + env(safe-area-inset-bottom)); |
||||||
|
background: #fff; |
||||||
|
display: flex; |
||||||
|
gap: 32rpx; |
||||||
|
|
||||||
|
.btn { |
||||||
|
flex: 1; |
||||||
|
height: 88rpx; |
||||||
|
border-radius: 44rpx; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
font-size: 32rpx; |
||||||
|
font-weight: 500; |
||||||
|
|
||||||
|
&-delete { |
||||||
|
border: 1px solid #b982ff; |
||||||
|
color: #b982ff; |
||||||
|
background: #fff; |
||||||
|
} |
||||||
|
|
||||||
|
&-retake { |
||||||
|
background: linear-gradient(0deg, #e98ff8 0%, #b073ff 100%); |
||||||
|
color: #fff; |
||||||
|
} |
||||||
|
|
||||||
|
&:active { |
||||||
|
opacity: 0.8; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,157 @@ |
|||||||
|
Component({ |
||||||
|
properties: { |
||||||
|
visible: { |
||||||
|
type: Boolean, |
||||||
|
value: false, |
||||||
|
observer(newVal) { |
||||||
|
this.setData({ visible: newVal }) |
||||||
|
}, |
||||||
|
}, |
||||||
|
src: { |
||||||
|
type: String, |
||||||
|
value: '', |
||||||
|
}, |
||||||
|
images: { |
||||||
|
type: Array, |
||||||
|
value: [], |
||||||
|
}, |
||||||
|
currentIndex: { |
||||||
|
type: Number, |
||||||
|
value: 0, |
||||||
|
}, |
||||||
|
photoLabels: { |
||||||
|
type: Array, |
||||||
|
value: [] as { name: string; index: number; total: number }[], |
||||||
|
}, |
||||||
|
showActions: { |
||||||
|
type: Boolean, |
||||||
|
value: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
|
||||||
|
data: { |
||||||
|
visible: false, |
||||||
|
src: '', |
||||||
|
images: [] as string[], |
||||||
|
currentIndex: 0, |
||||||
|
navHeight: wx.getSystemInfoSync().statusBarHeight + 44, |
||||||
|
startX: 0, |
||||||
|
startY: 0, |
||||||
|
}, |
||||||
|
|
||||||
|
observers: { |
||||||
|
'currentIndex, images': function (currentIndex: number, images: string[]) { |
||||||
|
if (images && images.length > 0 && images[currentIndex]) { |
||||||
|
this.setData({ |
||||||
|
src: images[currentIndex], |
||||||
|
}) |
||||||
|
} |
||||||
|
}, |
||||||
|
}, |
||||||
|
|
||||||
|
methods: { |
||||||
|
// 返回/关闭
|
||||||
|
handleBack() { |
||||||
|
this.triggerEvent('close') |
||||||
|
}, |
||||||
|
|
||||||
|
// 更多操作
|
||||||
|
handleMore() { |
||||||
|
this.triggerEvent('more') |
||||||
|
}, |
||||||
|
|
||||||
|
// 预览/查看
|
||||||
|
handlePreview(src) { |
||||||
|
if (src) { |
||||||
|
this.setData({ |
||||||
|
visible: true, |
||||||
|
src, |
||||||
|
}) |
||||||
|
} |
||||||
|
}, |
||||||
|
handleHidePreview() { |
||||||
|
this.setData({ |
||||||
|
visible: false, |
||||||
|
src: '', |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
// 上一张
|
||||||
|
handlePrev() { |
||||||
|
const { currentIndex, images } = this.data |
||||||
|
if (currentIndex > 0) { |
||||||
|
const newIndex = currentIndex - 1 |
||||||
|
this.setData({ |
||||||
|
currentIndex: newIndex, |
||||||
|
}) |
||||||
|
this.triggerEvent('change', { index: newIndex }) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// 下一张
|
||||||
|
handleNext() { |
||||||
|
const { currentIndex, images } = this.data |
||||||
|
if (currentIndex < images.length - 1) { |
||||||
|
const newIndex = currentIndex + 1 |
||||||
|
this.setData({ |
||||||
|
currentIndex: newIndex, |
||||||
|
}) |
||||||
|
this.triggerEvent('change', { index: newIndex }) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// 滑动手势 - 开始
|
||||||
|
handleTouchStart(e: any) { |
||||||
|
this.setData({ |
||||||
|
startX: e.touches[0].clientX, |
||||||
|
startY: e.touches[0].clientY, |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
// 滑动手势 - 结束
|
||||||
|
handleTouchEnd(e: any) { |
||||||
|
const { startX, startY, currentIndex, images } = this.data |
||||||
|
const endX = e.changedTouches[0].clientX |
||||||
|
const endY = e.changedTouches[0].clientY |
||||||
|
const diffX = endX - startX |
||||||
|
const diffY = endY - startY |
||||||
|
|
||||||
|
// 水平滑动距离大于50且大于垂直滑动距离时才触发切换
|
||||||
|
if (Math.abs(diffX) > 50 && Math.abs(diffX) > Math.abs(diffY)) { |
||||||
|
if (diffX > 0) { |
||||||
|
// 右滑 - 上一张
|
||||||
|
if (currentIndex > 0) { |
||||||
|
this.handlePrev() |
||||||
|
} |
||||||
|
} else { |
||||||
|
// 左滑 - 下一张
|
||||||
|
if (currentIndex < images.length - 1) { |
||||||
|
this.handleNext() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// 删除
|
||||||
|
handleDelete() { |
||||||
|
wx.showModal({ |
||||||
|
title: '提示', |
||||||
|
content: '确定要删除这张照片吗?', |
||||||
|
confirmColor: '#8c75d0', |
||||||
|
cancelColor: '#141515', |
||||||
|
success: (res) => { |
||||||
|
if (res.confirm) { |
||||||
|
this.handleHidePreview() |
||||||
|
this.triggerEvent('delete') |
||||||
|
} |
||||||
|
}, |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
// 重拍
|
||||||
|
handleRetake() { |
||||||
|
this.handleHidePreview() |
||||||
|
this.triggerEvent('retake') |
||||||
|
}, |
||||||
|
}, |
||||||
|
}) |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
<view class="preview-container {{visible ? 'show' : ''}}"> |
||||||
|
<navbar fixed title="" custom-style="background:transparent" bind:click-left="handleBack"> |
||||||
|
<van-icon name="arrow-left" slot="left" size="18px" color="#fff" bind:tap="handleBack" /> |
||||||
|
</navbar> |
||||||
|
|
||||||
|
<view class="image-wrapper" style="padding-top:{{navHeight}}px;" bindtouchstart="handleTouchStart" bindtouchend="handleTouchEnd"> |
||||||
|
<view class="order" wx:if="{{photoLabels[currentIndex]}}" style="top:{{navHeight * 0.5}}px;"> |
||||||
|
{{photoLabels[currentIndex].name}} |
||||||
|
<!-- <view class="num">{{photoLabels[currentIndex].index}}</view> --> |
||||||
|
<!-- <view class="m-num">/{{photoLabels[currentIndex].total}}</view> --> |
||||||
|
</view> |
||||||
|
<view class="nav-btn nav-prev {{currentIndex <= 0 ? 'disabled' : ''}}" wx:if="{{images.length > 1}}" bindtap="handlePrev"> |
||||||
|
<van-icon name="arrow-left" size="20px" color="{{currentIndex <= 0 ? '#666' : '#fff'}}" /> |
||||||
|
</view> |
||||||
|
<image class="preview-image" src="{{src}}" mode="aspectFit" /> |
||||||
|
<view class="nav-btn nav-next {{currentIndex >= images.length - 1 ? 'disabled' : ''}}" wx:if="{{images.length > 1}}" bindtap="handleNext"> |
||||||
|
<van-icon name="arrow" size="20px" color="{{currentIndex >= images.length - 1 ? '#666' : '#fff'}}" /> |
||||||
|
</view> |
||||||
|
</view> |
||||||
|
|
||||||
|
<view class="indicator" wx:if="{{images.length > 1}}"> |
||||||
|
<text class="current">{{currentIndex + 1}}</text> |
||||||
|
<text class="separator">/</text> |
||||||
|
<text class="total">{{images.length}}</text> |
||||||
|
</view> |
||||||
|
|
||||||
|
<view class="action-bar" wx:if="{{showActions}}"> |
||||||
|
<view class="btn btn-delete" bindtap="handleDelete">删除</view> |
||||||
|
<view class="btn btn-retake" bindtap="handleRetake">重拍</view> |
||||||
|
</view> |
||||||
|
</view> |
||||||
@ -1,95 +0,0 @@ |
|||||||
.from { |
|
||||||
padding: 48rpx 40rpx; |
|
||||||
width: 650rpx; |
|
||||||
box-sizing: border-box; |
|
||||||
background: linear-gradient(349deg, #ffffff 0%, #e2f1f4 100%); |
|
||||||
border-radius: 24rpx 24rpx 24rpx 24rpx; |
|
||||||
border: 2rpx solid #ffffff; |
|
||||||
.title { |
|
||||||
font-size: 32rpx; |
|
||||||
color: #283031; |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
.date { |
|
||||||
margin-top: 24rpx; |
|
||||||
padding: 14rpx 32rpx; |
|
||||||
display: flex; |
|
||||||
align-items: center; |
|
||||||
justify-content: space-between; |
|
||||||
background-color: #f2f4f5; |
|
||||||
border-radius: 16rpx; |
|
||||||
|
|
||||||
.conetent { |
|
||||||
font-size: 32rpx; |
|
||||||
color: #283031; |
|
||||||
} |
|
||||||
.tril { |
|
||||||
width: 0; |
|
||||||
height: 0; |
|
||||||
border-style: solid; |
|
||||||
border-width: 10rpx 10rpx 0 10rpx; |
|
||||||
border-color: #aeb3b4 transparent transparent transparent; |
|
||||||
} |
|
||||||
} |
|
||||||
.select-title { |
|
||||||
margin-top: 48rpx; |
|
||||||
font-size: 32rpx; |
|
||||||
color: #283031; |
|
||||||
font-weight: bold; |
|
||||||
.sub { |
|
||||||
font-weight: normal; |
|
||||||
} |
|
||||||
} |
|
||||||
.list { |
|
||||||
margin-top: 26rpx; |
|
||||||
max-height: 55vh; |
|
||||||
overflow-y: auto; |
|
||||||
&::-webkit-scrollbar{ |
|
||||||
display: none; |
|
||||||
} |
|
||||||
.item { |
|
||||||
margin-bottom: 16rpx; |
|
||||||
padding: 14rpx 32rpx; |
|
||||||
font-size: 32rpx; |
|
||||||
color: #283031; |
|
||||||
line-height: 48rpx; |
|
||||||
background-color: #f2f4f5; |
|
||||||
border: 1px solid #f2f4f5; |
|
||||||
border-radius: 16rpx; |
|
||||||
&.active { |
|
||||||
border-color: #67baca; |
|
||||||
background-color: #e7f5f8; |
|
||||||
color: #67baca; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
.footer { |
|
||||||
margin-top: 32rpx; |
|
||||||
display: flex; |
|
||||||
justify-content: space-between; |
|
||||||
align-items: center; |
|
||||||
gap: 26rpx; |
|
||||||
text-align: center; |
|
||||||
.cancel { |
|
||||||
flex: 1; |
|
||||||
height: 80rpx; |
|
||||||
font-size: 36rpx; |
|
||||||
color: #67BACA; |
|
||||||
line-height: 80rpx; |
|
||||||
background: #ffffff; |
|
||||||
border-radius: 98rpx 98rpx 98rpx 98rpx; |
|
||||||
border: 2rpx solid #67baca; |
|
||||||
} |
|
||||||
|
|
||||||
.submit { |
|
||||||
flex: 1; |
|
||||||
height: 80rpx; |
|
||||||
font-size: 36rpx; |
|
||||||
color: #FFFFFF; |
|
||||||
line-height: 80rpx; |
|
||||||
background: #67baca; |
|
||||||
border-radius: 98rpx 98rpx 98rpx 98rpx; |
|
||||||
border: 2rpx solid #67baca; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,139 +0,0 @@ |
|||||||
import dayjs from 'dayjs' |
|
||||||
|
|
||||||
const _app = getApp<IAppOption>() |
|
||||||
|
|
||||||
// pages/story/a.ts
|
|
||||||
Component({ |
|
||||||
/** |
|
||||||
* 组件的属性列表 |
|
||||||
*/ |
|
||||||
properties: { |
|
||||||
show: { |
|
||||||
type: Boolean, |
|
||||||
value: false, |
|
||||||
}, |
|
||||||
params: { |
|
||||||
type: Object, |
|
||||||
value: undefined, |
|
||||||
}, |
|
||||||
}, |
|
||||||
|
|
||||||
observers: { |
|
||||||
show() { |
|
||||||
if (this.data.params) { |
|
||||||
this.setData({ |
|
||||||
...this.data.params, |
|
||||||
}) |
|
||||||
this.handleDateChange() |
|
||||||
} else { |
|
||||||
this.setData({ |
|
||||||
visitDateName: '', |
|
||||||
visitDate: '', |
|
||||||
hormone: 2, |
|
||||||
traditionalInhibitor: 2, |
|
||||||
gammaGlobulin: 2, |
|
||||||
plasmaExchange: 2, |
|
||||||
bCellInhibitor: 2, |
|
||||||
fcRnAntagonists: 2, |
|
||||||
c5ComplementInhibitor: 2, |
|
||||||
chineseMedicine: 2, |
|
||||||
other: 2, |
|
||||||
recordId: '', |
|
||||||
}) |
|
||||||
} |
|
||||||
}, |
|
||||||
}, |
|
||||||
data: { |
|
||||||
currentDate: dayjs().format('YYYY-MM-DD'), |
|
||||||
|
|
||||||
visitDateName: '', |
|
||||||
visitDate: '', |
|
||||||
hormone: 2, |
|
||||||
traditionalInhibitor: 2, |
|
||||||
gammaGlobulin: 2, |
|
||||||
plasmaExchange: 2, |
|
||||||
bCellInhibitor: 2, |
|
||||||
fcRnAntagonists: 2, |
|
||||||
c5ComplementInhibitor: 2, |
|
||||||
chineseMedicine: 2, |
|
||||||
other: 2, |
|
||||||
recordId: '', |
|
||||||
}, |
|
||||||
|
|
||||||
methods: { |
|
||||||
handleDateChange() { |
|
||||||
this.setData({ |
|
||||||
visitDateName: dayjs(this.data.visitDate).format('YYYY年MM月DD日'), |
|
||||||
}) |
|
||||||
}, |
|
||||||
handleSelect(e) { |
|
||||||
const { name } = e.currentTarget.dataset |
|
||||||
const value = this.data[name] |
|
||||||
this.setData({ |
|
||||||
[name]: value === 2 ? 1 : 2, |
|
||||||
}) |
|
||||||
}, |
|
||||||
submit() { |
|
||||||
const { visitDate, recordId } = this.data |
|
||||||
const params = { |
|
||||||
visitDate, |
|
||||||
recordId, |
|
||||||
} |
|
||||||
if(!visitDate){ |
|
||||||
wx.showToast({ |
|
||||||
title: '请选择复诊日期', |
|
||||||
icon: 'none', |
|
||||||
}) |
|
||||||
return |
|
||||||
} |
|
||||||
const selectKeys = [ |
|
||||||
'hormone', |
|
||||||
'traditionalInhibitor', |
|
||||||
'gammaGlobulin', |
|
||||||
'plasmaExchange', |
|
||||||
'bCellInhibitor', |
|
||||||
'fcRnAntagonists', |
|
||||||
'c5ComplementInhibitor', |
|
||||||
'chineseMedicine', |
|
||||||
'other', |
|
||||||
] |
|
||||||
selectKeys.forEach((item) => { |
|
||||||
params[item] = this.data[item] |
|
||||||
}) |
|
||||||
const onlySelect = selectKeys.some((item) => { |
|
||||||
return this.data[item] === 1 |
|
||||||
}) |
|
||||||
if (!onlySelect) { |
|
||||||
wx.showToast({ |
|
||||||
title: '请至少选择一种复诊后的方案', |
|
||||||
icon: 'none', |
|
||||||
}) |
|
||||||
return |
|
||||||
} |
|
||||||
wx.ajax({ |
|
||||||
method: 'POST', |
|
||||||
url: '?r=xd/re-visit/save-record', |
|
||||||
data: params, |
|
||||||
}).then(() => { |
|
||||||
if (recordId) { |
|
||||||
wx.showToast({ |
|
||||||
icon: 'none', |
|
||||||
title: '编辑成功', |
|
||||||
}) |
|
||||||
} else { |
|
||||||
wx.showToast({ |
|
||||||
icon: 'none', |
|
||||||
title: '添加成功', |
|
||||||
}) |
|
||||||
} |
|
||||||
this.handleCancel() |
|
||||||
this.triggerEvent('refresh', params) |
|
||||||
}) |
|
||||||
}, |
|
||||||
handleCancel() { |
|
||||||
this.setData({ |
|
||||||
show: false, |
|
||||||
}) |
|
||||||
}, |
|
||||||
}, |
|
||||||
}) |
|
||||||
@ -1,52 +0,0 @@ |
|||||||
<van-popup custom-style="background: transparent;" round z-index="{{100000}}" show="{{ show }}"> |
|
||||||
<view class="from"> |
|
||||||
<view class="title">您上一次复诊时间?</view> |
|
||||||
<picker mode="date" model:value="{{visitDate}}" end="{{currentDate}}" bind:change="handleDateChange"> |
|
||||||
<view class="date"> |
|
||||||
<view class="content">{{visitDateName||'请选择'}}</view> |
|
||||||
<view class="tril"></view> |
|
||||||
</view> |
|
||||||
</picker> |
|
||||||
<view class="select-title"> |
|
||||||
您复诊后的方案是? |
|
||||||
<text class="sub">(多选)</text> |
|
||||||
</view> |
|
||||||
<view class="list"> |
|
||||||
<view bind:tap="handleSelect" data-name="hormone" class="item {{hormone===1 && 'active'}}">1.激素</view> |
|
||||||
<view |
|
||||||
bind:tap="handleSelect" |
|
||||||
data-name="traditionalInhibitor" |
|
||||||
class="item {{traditionalInhibitor===1 && 'active'}}" |
|
||||||
> |
|
||||||
2.传统免疫抑制剂(如他克莫司、吗 替麦考酚酯等) |
|
||||||
</view> |
|
||||||
<view bind:tap="handleSelect" data-name="gammaGlobulin" class="item {{gammaGlobulin===1 && 'active'}}"> |
|
||||||
3.静脉输注丙种球蛋白 |
|
||||||
</view> |
|
||||||
<view bind:tap="handleSelect" data-name="plasmaExchange" class="item {{plasmaExchange===1 && 'active'}}"> |
|
||||||
4.血浆置换 |
|
||||||
</view> |
|
||||||
<view bind:tap="handleSelect" data-name="bCellInhibitor" class="item {{bCellInhibitor===1 && 'active'}}"> |
|
||||||
5.B细胞抑制剂(如:利妥昔单抗、泰 它西普、伊奈利珠单抗) |
|
||||||
</view> |
|
||||||
<view bind:tap="handleSelect" data-name="fcRnAntagonists" class="item {{fcRnAntagonists===1 && 'active'}}"> |
|
||||||
6.FcRn拮抗剂(如:艾加莫德) |
|
||||||
</view> |
|
||||||
<view |
|
||||||
bind:tap="handleSelect" |
|
||||||
data-name="c5ComplementInhibitor" |
|
||||||
class="item {{c5ComplementInhibitor===1 && 'active'}}" |
|
||||||
> |
|
||||||
7.C5补体抑制剂(如:依库珠单抗) |
|
||||||
</view> |
|
||||||
<view bind:tap="handleSelect" data-name="chineseMedicine" class="item {{chineseMedicine===1 && 'active'}}"> |
|
||||||
8.中药或中成药 |
|
||||||
</view> |
|
||||||
<view bind:tap="handleSelect" data-name="other" class="item {{other===1 && 'active'}}">9.其他</view> |
|
||||||
</view> |
|
||||||
<view class="footer"> |
|
||||||
<view class="cancel" bind:tap="handleCancel">取消</view> |
|
||||||
<view class="submit" bind:tap="submit">确定</view> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
</van-popup> |
|
||||||
@ -0,0 +1,12 @@ |
|||||||
|
export default { |
||||||
|
wxf9ce8010f1ad24aa: { |
||||||
|
url: 'https://m.xd.hbraas.com', |
||||||
|
upFileUrl: 'https://m.xd.hbraas.com/', |
||||||
|
imageUrl: 'https://m.xd.hbraas.com/xd/', |
||||||
|
}, |
||||||
|
wx71ac9c27c3c3e3f4: { |
||||||
|
url: 'https://m.xd.hbsaas.com', |
||||||
|
upFileUrl: 'https://m.xd.hbsaas.com/', |
||||||
|
imageUrl: 'https://m.xd.hbsaas.com/api/xd/', |
||||||
|
}, |
||||||
|
} |
||||||
@ -1,3 +1,3 @@ |
|||||||
.page{ |
.page { |
||||||
padding: 0 40rpx; |
padding: 0 40rpx; |
||||||
} |
} |
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,3 +1,3 @@ |
|||||||
.mp-html{ |
.mp-html { |
||||||
padding: 20rpx 40rpx; |
padding: 20rpx 40rpx; |
||||||
} |
} |
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,6 +0,0 @@ |
|||||||
{ |
|
||||||
"navigationBarTitleText": "确认订单", |
|
||||||
"usingComponents": { |
|
||||||
"van-icon": "@vant/weapp/icon/index" |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,184 +0,0 @@ |
|||||||
.page { |
|
||||||
padding: 34rpx 40rpx; |
|
||||||
.site { |
|
||||||
padding: 40rpx 32rpx; |
|
||||||
background: #ffffff; |
|
||||||
box-shadow: 0rpx 4rpx 20rpx 0rpx rgba(0, 0, 0, 0.05); |
|
||||||
border-radius: 24rpx; |
|
||||||
display: flex; |
|
||||||
align-items: center; |
|
||||||
justify-content: space-between; |
|
||||||
.wrap { |
|
||||||
.title { |
|
||||||
.label { |
|
||||||
width: 72rpx; |
|
||||||
height: 36rpx; |
|
||||||
border: 1rpx solid #e04775; |
|
||||||
font-size: 24rpx; |
|
||||||
color: #e04775; |
|
||||||
text-align: center; |
|
||||||
border-radius: 10rpx; |
|
||||||
box-sizing: border-box; |
|
||||||
} |
|
||||||
.name { |
|
||||||
margin-top: -42rpx; |
|
||||||
text-indent: 80rpx; |
|
||||||
font-size: 36rpx; |
|
||||||
line-height: 46rpx; |
|
||||||
color: #3f3f3f; |
|
||||||
font-weight: bold; |
|
||||||
min-width: 0; |
|
||||||
overflow: hidden; |
|
||||||
text-overflow: ellipsis; |
|
||||||
display: -webkit-box; |
|
||||||
-webkit-line-clamp: 2; |
|
||||||
-webkit-box-orient: vertical; |
|
||||||
&.no-indent { |
|
||||||
margin-top: 0; |
|
||||||
text-indent: 0; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
.content { |
|
||||||
margin-top: 0; |
|
||||||
margin-top: 12rpx; |
|
||||||
font-size: 28rpx; |
|
||||||
color: #b6b7ba; |
|
||||||
} |
|
||||||
} |
|
||||||
.more { |
|
||||||
flex-shrink: 0; |
|
||||||
} |
|
||||||
} |
|
||||||
.shop { |
|
||||||
margin-top: 16px; |
|
||||||
padding: 40rpx 32rpx; |
|
||||||
background: #ffffff; |
|
||||||
box-shadow: 0rpx 4rpx 20rpx 0rpx rgba(0, 0, 0, 0.05); |
|
||||||
border-radius: 24rpx; |
|
||||||
.shop-header { |
|
||||||
padding-bottom: 10px; |
|
||||||
display: flex; |
|
||||||
.shop-img { |
|
||||||
flex-shrink: 0; |
|
||||||
width: 204rpx; |
|
||||||
height: 204rpx; |
|
||||||
border-radius: 24rpx; |
|
||||||
} |
|
||||||
.wrap { |
|
||||||
padding-top: 8rpx; |
|
||||||
flex: 1; |
|
||||||
padding-left: 24rpx; |
|
||||||
.name { |
|
||||||
font-size: 32rpx; |
|
||||||
font-weight: bold; |
|
||||||
color: #3f3f3f; |
|
||||||
line-height: 44rpx; |
|
||||||
min-width: 0; |
|
||||||
overflow: hidden; |
|
||||||
text-overflow: ellipsis; |
|
||||||
display: -webkit-box; |
|
||||||
-webkit-line-clamp: 2; |
|
||||||
-webkit-box-orient: vertical; |
|
||||||
} |
|
||||||
.specification { |
|
||||||
margin-top: 8rpx; |
|
||||||
font-size: 28rpx; |
|
||||||
color: #b6b7ba; |
|
||||||
} |
|
||||||
.price { |
|
||||||
margin-top: 14rpx; |
|
||||||
display: flex; |
|
||||||
align-items: center; |
|
||||||
justify-content: space-between; |
|
||||||
.num { |
|
||||||
font-size: 32rpx; |
|
||||||
color: #3f3f3f; |
|
||||||
} |
|
||||||
.sub { |
|
||||||
font-size: 22rpx; |
|
||||||
} |
|
||||||
.val { |
|
||||||
font-size: 28rpx; |
|
||||||
color: #b6b7ba; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
.row { |
|
||||||
margin-top: 32rpx; |
|
||||||
display: flex; |
|
||||||
align-items: center; |
|
||||||
justify-content: space-between; |
|
||||||
.label { |
|
||||||
font-size: 32rpx; |
|
||||||
color: #3f3f3f; |
|
||||||
} |
|
||||||
.content { |
|
||||||
font-size: 32rpx; |
|
||||||
color: #3f3f3f; |
|
||||||
&.yellow { |
|
||||||
color: #e04775; |
|
||||||
} |
|
||||||
.sub { |
|
||||||
font-size: 22rpx; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
.remark { |
|
||||||
margin-top: 16px; |
|
||||||
padding: 40rpx 32rpx; |
|
||||||
background: #ffffff; |
|
||||||
box-shadow: 0rpx 4rpx 20rpx 0rpx rgba(0, 0, 0, 0.05); |
|
||||||
border-radius: 24rpx; |
|
||||||
.title { |
|
||||||
font-size: 32rpx; |
|
||||||
color: #3f3f3f; |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
.textarea { |
|
||||||
margin-top: 10rpx; |
|
||||||
padding: 24rpx 32rpx; |
|
||||||
background-color: #fafafa; |
|
||||||
min-height: 196rpx; |
|
||||||
border-radius: 16rpx; |
|
||||||
box-sizing: border-box; |
|
||||||
} |
|
||||||
} |
|
||||||
.footer { |
|
||||||
padding: 24rpx 48rpx 48rpx; |
|
||||||
position: fixed; |
|
||||||
bottom: 0; |
|
||||||
left: 0; |
|
||||||
width: 100vw; |
|
||||||
box-sizing: border-box; |
|
||||||
display: flex; |
|
||||||
justify-content: space-between; |
|
||||||
align-items: center; |
|
||||||
height: 168rpx; |
|
||||||
background: #ffffff; |
|
||||||
box-shadow: 0rpx 8rpx 20rpx 0rpx rgba(0, 0, 0, 0.26); |
|
||||||
.price { |
|
||||||
font-size: 28rpx; |
|
||||||
color: #b6b7ba; |
|
||||||
.num { |
|
||||||
font-size: 40rpx; |
|
||||||
color: #e04775; |
|
||||||
} |
|
||||||
.sub { |
|
||||||
font-size: 24rpx; |
|
||||||
} |
|
||||||
} |
|
||||||
.submit { |
|
||||||
width: 260rpx; |
|
||||||
height: 96rpx; |
|
||||||
background: #e04775; |
|
||||||
border-radius: 48rpx; |
|
||||||
text-align: center; |
|
||||||
line-height: 96rpx; |
|
||||||
color: #fff; |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,86 +0,0 @@ |
|||||||
const app = getApp<IAppOption>(); |
|
||||||
|
|
||||||
Page({ |
|
||||||
data: { |
|
||||||
id: "", |
|
||||||
detail: {}, |
|
||||||
select: false, |
|
||||||
addressDetail: {} as any, |
|
||||||
remark: "", |
|
||||||
}, |
|
||||||
onLoad(options) { |
|
||||||
this.setData({ |
|
||||||
id: options.id, |
|
||||||
}); |
|
||||||
}, |
|
||||||
onShow() { |
|
||||||
app.waitLogin().then(() => { |
|
||||||
this.getDetail(); |
|
||||||
if (!this.data.select) { |
|
||||||
this.getDefaultAddress(); |
|
||||||
} else { |
|
||||||
this.setData({ |
|
||||||
select: false, |
|
||||||
}); |
|
||||||
} |
|
||||||
}); |
|
||||||
}, |
|
||||||
getDefaultAddress() { |
|
||||||
wx.ajax({ |
|
||||||
method: "GET", |
|
||||||
url: "?r=zd/patient-address/get-default-address", |
|
||||||
data: {}, |
|
||||||
}).then((res) => { |
|
||||||
this.setData({ |
|
||||||
addressDetail: res, |
|
||||||
}); |
|
||||||
}); |
|
||||||
}, |
|
||||||
getDetail() { |
|
||||||
wx.ajax({ |
|
||||||
method: "GET", |
|
||||||
url: "?r=zd/gift-order/get-order-detail", |
|
||||||
data: { |
|
||||||
orderId: this.data.id, |
|
||||||
}, |
|
||||||
}).then((res) => { |
|
||||||
this.setData({ |
|
||||||
detail: res, |
|
||||||
}); |
|
||||||
}); |
|
||||||
}, |
|
||||||
handleSite() { |
|
||||||
if (this.data.addressDetail) { |
|
||||||
wx.navigateTo({ |
|
||||||
url: "/gift/pages/siteList/index", |
|
||||||
}); |
|
||||||
} else { |
|
||||||
wx.navigateTo({ |
|
||||||
url: "/gift/pages/siteEdit/index", |
|
||||||
}); |
|
||||||
} |
|
||||||
}, |
|
||||||
handleSubmit() { |
|
||||||
if (!this.data.addressDetail?.addressId) { |
|
||||||
wx.showToast({ |
|
||||||
icon: "none", |
|
||||||
title: "请选择地址", |
|
||||||
}); |
|
||||||
return; |
|
||||||
} |
|
||||||
wx.ajax({ |
|
||||||
method: "POST", |
|
||||||
url: "?r=zd/gift-order/confirm-order", |
|
||||||
data: { |
|
||||||
orderId: this.data.id, |
|
||||||
addressId: this.data.addressDetail.addressId, |
|
||||||
remark: this.data.remark, |
|
||||||
}, |
|
||||||
loading: true, |
|
||||||
}).then(() => { |
|
||||||
wx.reLaunch({ |
|
||||||
url: `/gift/pages/orderEnd/index?id=${this.data.id}`, |
|
||||||
}); |
|
||||||
}); |
|
||||||
}, |
|
||||||
}); |
|
||||||
@ -1,56 +0,0 @@ |
|||||||
<view class="page"> |
|
||||||
<view class="site" bind:tap="handleSite"> |
|
||||||
<view class="wrap"> |
|
||||||
<block wx:if="{{addressDetail}}"> |
|
||||||
<view class="title"> |
|
||||||
<view class="label" wx:if="{{addressDetail.isDefault==1}}">默认</view> |
|
||||||
<view class="name {{addressDetail.isDefault!=1 && 'no-indent'}}"> |
|
||||||
{{addressDetail.provinceName}}{{addressDetail.cityName}}{{addressDetail.countyName}}{{addressDetail.address}} |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
<view class="content">{{addressDetail.receiveUserName}} {{addressDetail.receiveTelephone}}</view> |
|
||||||
</block> |
|
||||||
<view class="title" wx:else>请添加收货地址</view> |
|
||||||
</view> |
|
||||||
<van-icon class="more" name="arrow" /> |
|
||||||
</view> |
|
||||||
<view class="shop"> |
|
||||||
<view class="shop-header"> |
|
||||||
<image class="shop-img" src="{{detail.giftBigImg}}"></image> |
|
||||||
<view class="wrap"> |
|
||||||
<view class="name">{{detail.giftName}}</view> |
|
||||||
<view class="specification" wx:if="{{detail.specName}}">规格:{{detail.specName}}</view> |
|
||||||
<view class="price"> |
|
||||||
<view class="num">{{detail.giftScore}}<text class="sub">能量</text></view> |
|
||||||
<view class="val">x{{detail.orderCount}}</view> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
<view class="row"> |
|
||||||
<view class="label">礼品总价</view> |
|
||||||
<view class="content yellow">{{detail.orderScore}}<text class="sub">能量</text></view> |
|
||||||
</view> |
|
||||||
<view class="row" > |
|
||||||
<view class="label">配送方式</view> |
|
||||||
<view class="content" style="font-weight:normal">快递配送</view> |
|
||||||
</view> |
|
||||||
<view class="row" > |
|
||||||
<view class="label">商家电话</view> |
|
||||||
<view class="content" style="font-weight:normal">{{detail.serviceTel}}</view> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
<view class="remark"> |
|
||||||
<view class="title">留言</view> |
|
||||||
<textarea class="textarea" model:value="{{remark}}" placeholder="请输入留言" auto-height></textarea> |
|
||||||
</view> |
|
||||||
<view class="footer"> |
|
||||||
<view class="price"> |
|
||||||
共{{detail.orderCount}}件 |
|
||||||
<view> |
|
||||||
合计消耗 |
|
||||||
<text class="num">{{detail.orderScore}}<text class="sub">能量</text></text> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
<view class="submit" bind:tap="handleSubmit">提交订单</view> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
{ |
|
||||||
"navigationBarTitleText": "皮下剂型", |
|
||||||
"usingComponents": {} |
|
||||||
} |
|
||||||
@ -1,63 +0,0 @@ |
|||||||
page { |
|
||||||
background-color: #f7f6f7; |
|
||||||
} |
|
||||||
.page { |
|
||||||
.page1 { |
|
||||||
height: 869rpx; |
|
||||||
} |
|
||||||
.page2 { |
|
||||||
position: relative; |
|
||||||
height: 630rpx; |
|
||||||
.mg-video { |
|
||||||
position: absolute; |
|
||||||
top: 290rpx; |
|
||||||
right: 60rpx; |
|
||||||
width: 600rpx; |
|
||||||
height: 288rpx; |
|
||||||
} |
|
||||||
} |
|
||||||
.page3 { |
|
||||||
position: relative; |
|
||||||
height: 782rpx; |
|
||||||
.cidp-mask { |
|
||||||
position: absolute; |
|
||||||
top: 174rpx; |
|
||||||
right: 30rpx; |
|
||||||
z-index: 1; |
|
||||||
width: 74rpx; |
|
||||||
height: 304rpx; |
|
||||||
} |
|
||||||
.cidp-list { |
|
||||||
position: absolute; |
|
||||||
top: 184rpx; |
|
||||||
right: 30rpx; |
|
||||||
width: 632rpx; |
|
||||||
height: 288rpx; |
|
||||||
overflow-x: auto; |
|
||||||
overflow-y: hidden; |
|
||||||
display: flex; |
|
||||||
gap: 16rpx; |
|
||||||
.cidp-item { |
|
||||||
flex-shrink: 0; |
|
||||||
width: 216rpx; |
|
||||||
height: 288rpx; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
.page4 { |
|
||||||
height: 302rpx; |
|
||||||
} |
|
||||||
.page5 { |
|
||||||
height: 306rpx; |
|
||||||
} |
|
||||||
.page6 { |
|
||||||
display: block; |
|
||||||
width: 100%; |
|
||||||
height: 574rpx; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#video { |
|
||||||
width: 0; |
|
||||||
height: 0; |
|
||||||
} |
|
||||||
@ -1,65 +0,0 @@ |
|||||||
const _app = getApp<IAppOption>() |
|
||||||
|
|
||||||
Page({ |
|
||||||
data: { |
|
||||||
mg: [ |
|
||||||
{ |
|
||||||
url: 'https://circlehbsaas.oss-cn-beijing.aliyuncs.com/video/20241129469_MG%E4%B8%93%E5%8C%BA%E8%A7%86%E9%A2%9101.mp4', |
|
||||||
}, |
|
||||||
], |
|
||||||
cidp: [ |
|
||||||
{ |
|
||||||
url: 'https://circlehbsaas.oss-cn-beijing.aliyuncs.com/video/20241129233_CIDP%E4%B8%93%E5%8C%BA%E8%A7%86%E9%A2%9101.mp4', |
|
||||||
}, |
|
||||||
{ |
|
||||||
url: 'https://circlehbsaas.oss-cn-beijing.aliyuncs.com/video/20241129667_CIDP%E4%B8%93%E5%8C%BA%E8%A7%86%E9%A2%9103.mp4', |
|
||||||
}, |
|
||||||
{ |
|
||||||
url: 'https://circlehbsaas.oss-cn-beijing.aliyuncs.com/video/20241129630_CIDP%E8%A7%86%E9%A2%91%E4%B8%93%E5%8C%BA02.mp4', |
|
||||||
}, |
|
||||||
{ |
|
||||||
url: 'https://circlehbsaas.oss-cn-beijing.aliyuncs.com/video/20241129281_CIDP%E4%B8%93%E5%8C%BA%E8%A7%86%E9%A2%9104.mp4', |
|
||||||
}, |
|
||||||
], |
|
||||||
}, |
|
||||||
videoContext: null as WechatMiniprogram.VideoContext | null, |
|
||||||
onLoad() { |
|
||||||
this.videoContext = wx.createVideoContext('video') |
|
||||||
}, |
|
||||||
onUnload() { |
|
||||||
if (this.videoContext) { |
|
||||||
this.videoContext.stop() |
|
||||||
} |
|
||||||
}, |
|
||||||
handleVideo(e: any) { |
|
||||||
const { key, index } = e.currentTarget.dataset |
|
||||||
const url = this.data[key][index].url |
|
||||||
if (this.videoContext) { |
|
||||||
this.setData({ |
|
||||||
videoUrl: url, |
|
||||||
}) |
|
||||||
this.videoContext.play() |
|
||||||
this.videoContext.requestFullScreen({ |
|
||||||
direction: 0, |
|
||||||
}) |
|
||||||
} |
|
||||||
}, |
|
||||||
handleFullScreen(e) { |
|
||||||
if (!e.detail.fullScreen && this.videoContext) { |
|
||||||
this.videoContext.stop() |
|
||||||
} |
|
||||||
}, |
|
||||||
handleDetail(e: any) { |
|
||||||
const { id } = e.currentTarget.dataset |
|
||||||
wx.navigateTo({ |
|
||||||
url: `/gift/pages/cutaneousDetail/index?id=${id}`, |
|
||||||
}) |
|
||||||
}, |
|
||||||
handleVideoDetail() { |
|
||||||
wx.navigateTo({ |
|
||||||
url: `/gift/pages/cutaneousVideo/index`, |
|
||||||
}) |
|
||||||
}, |
|
||||||
}) |
|
||||||
|
|
||||||
export {} |
|
||||||
@ -1,82 +0,0 @@ |
|||||||
<view class="page"> |
|
||||||
<view |
|
||||||
class="page1" |
|
||||||
style="background: url({{imageUrl}}za-images//cutaneous/home1.png?t={{Timestamp}}) no-repeat top center/100%;" |
|
||||||
></view> |
|
||||||
|
|
||||||
<view |
|
||||||
class="page2" |
|
||||||
style="background: url({{imageUrl}}za-images//cutaneous/home2.png?t={{Timestamp}}) no-repeat top center/100%;" |
|
||||||
bind:tap="handleDetail" |
|
||||||
data-id="2" |
|
||||||
> |
|
||||||
<image |
|
||||||
class="mg-video" |
|
||||||
src="{{imageUrl}}za-images//cutaneous/video1-1.png?t={{Timestamp}}" |
|
||||||
data-key="mg" |
|
||||||
data-index="0" |
|
||||||
catch:tap="handleVideo" |
|
||||||
></image> |
|
||||||
</view> |
|
||||||
<view |
|
||||||
class="page3" |
|
||||||
style="background: url({{imageUrl}}za-images//cutaneous/home3.png?t={{Timestamp}}) no-repeat top center/100%;" |
|
||||||
bind:tap="handleDetail" |
|
||||||
data-id="3" |
|
||||||
> |
|
||||||
<image class="cidp-mask" src="{{imageUrl}}za-images//cutaneous/cidp-mask.png?t={{Timestamp}}"></image> |
|
||||||
<view class="cidp-list"> |
|
||||||
<image |
|
||||||
class="cidp-item" |
|
||||||
src="{{imageUrl}}za-images//cutaneous/video2-1.png?t={{Timestamp}}" |
|
||||||
data-key="cidp" |
|
||||||
data-index="0" |
|
||||||
catch:tap="handleVideo" |
|
||||||
></image> |
|
||||||
<image |
|
||||||
class="cidp-item" |
|
||||||
src="{{imageUrl}}za-images//cutaneous/video2-2.png?t={{Timestamp}}" |
|
||||||
data-key="cidp" |
|
||||||
data-index="1" |
|
||||||
catch:tap="handleVideo" |
|
||||||
></image> |
|
||||||
<image |
|
||||||
class="cidp-item" |
|
||||||
src="{{imageUrl}}za-images//cutaneous/video2-3.png?t={{Timestamp}}" |
|
||||||
data-key="cidp" |
|
||||||
data-index="2" |
|
||||||
catch:tap="handleVideo" |
|
||||||
></image> |
|
||||||
<image |
|
||||||
class="cidp-item" |
|
||||||
src="{{imageUrl}}za-images//cutaneous/video2-4.png?t={{Timestamp}}" |
|
||||||
data-key="cidp" |
|
||||||
data-index="3" |
|
||||||
catch:tap="handleVideo" |
|
||||||
></image> |
|
||||||
</view> |
|
||||||
</view> |
|
||||||
<view |
|
||||||
class="page4" |
|
||||||
style="background: url({{imageUrl}}za-images//cutaneous/home4.png?t={{Timestamp}}) no-repeat top center/100%;" |
|
||||||
bind:tap="handleDetail" |
|
||||||
data-id="4" |
|
||||||
></view> |
|
||||||
<view |
|
||||||
class="page5" |
|
||||||
style="background: url({{imageUrl}}za-images//cutaneous/home5.png?t={{Timestamp}}) no-repeat top center/100%;" |
|
||||||
bind:tap="handleDetail" |
|
||||||
data-id="5" |
|
||||||
></view> |
|
||||||
<image class="page6" bind:tap="handleVideoDetail" src="{{imageUrl}}za-images//cutaneous/home6.png?t={{Timestamp}}"></image> |
|
||||||
</view> |
|
||||||
|
|
||||||
<video |
|
||||||
id="video" |
|
||||||
src="{{videoUrl}}" |
|
||||||
loop |
|
||||||
show-play-btn |
|
||||||
play-btn-position="center" |
|
||||||
enable-play-gesture |
|
||||||
bindfullscreenchange="handleFullScreen" |
|
||||||
></video> |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
{ |
|
||||||
"navigationBarTitleText": "皮下剂型", |
|
||||||
"usingComponents": {} |
|
||||||
} |
|
||||||
@ -1,11 +0,0 @@ |
|||||||
.page { |
|
||||||
.card { |
|
||||||
width: 100%; |
|
||||||
display: block; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#video { |
|
||||||
width: 0; |
|
||||||
height: 0; |
|
||||||
} |
|
||||||
@ -1,26 +0,0 @@ |
|||||||
const _app = getApp<IAppOption>() |
|
||||||
|
|
||||||
Page({ |
|
||||||
data: { |
|
||||||
list: { |
|
||||||
2: ['2-1', '2-2', '2-3', '2-4'], |
|
||||||
3: ['3-1', '3-2', '3-3', '3-4', '3-5'], |
|
||||||
4: ['4-1', '4-2'], |
|
||||||
5: ['5-1', '5-2', '5-3', '5-4', '5-5'], |
|
||||||
}, |
|
||||||
curentList: [] as any, |
|
||||||
}, |
|
||||||
onLoad(e) { |
|
||||||
if (e.id) { |
|
||||||
this.setData({ |
|
||||||
curentList: this.data.list[e.id], |
|
||||||
}) |
|
||||||
} else { |
|
||||||
this.setData({ |
|
||||||
curentList: this.data.list[2], |
|
||||||
}) |
|
||||||
} |
|
||||||
}, |
|
||||||
}) |
|
||||||
|
|
||||||
export {} |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
<view class="page"> |
|
||||||
<image |
|
||||||
class="card" |
|
||||||
mode="widthFix" |
|
||||||
wx:for="{{curentList}}" |
|
||||||
wx:key="index" |
|
||||||
src="{{imageUrl}}za-images//cutaneous/{{item}}.png?t={{Timestamp}}" |
|
||||||
></image> |
|
||||||
</view> |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
{ |
|
||||||
"navigationBarTitleText": "皮下剂型", |
|
||||||
"usingComponents": {} |
|
||||||
} |
|
||||||
@ -1,36 +0,0 @@ |
|||||||
page { |
|
||||||
background: linear-gradient(218deg, #e8bbe7 0%, #ffedf6 26%, #ffedf6 100%); |
|
||||||
} |
|
||||||
.page { |
|
||||||
padding: 408rpx 40rpx 0; |
|
||||||
.container { |
|
||||||
padding: 40rpx 32rpx 42rpx; |
|
||||||
background-color: #fff; |
|
||||||
border-radius: 32rpx; |
|
||||||
.content { |
|
||||||
font-size: 32rpx; |
|
||||||
line-height: 2; |
|
||||||
color: #002b48; |
|
||||||
} |
|
||||||
.tip { |
|
||||||
margin-top: 28rpx; |
|
||||||
font-size: 32rpx; |
|
||||||
color: rgba(87,87,87,0.5); |
|
||||||
} |
|
||||||
} |
|
||||||
.btn { |
|
||||||
margin-top: 42rpx; |
|
||||||
height: 92rpx; |
|
||||||
font-size: 32rpx; |
|
||||||
color: #FFFFFF; |
|
||||||
display: flex; |
|
||||||
align-items: center; |
|
||||||
justify-content: center; |
|
||||||
background: linear-gradient(90deg, #dd406a 0%, #9039b0 100%); |
|
||||||
border-radius: 112rpx 112rpx 112rpx 112rpx; |
|
||||||
} |
|
||||||
} |
|
||||||
#video { |
|
||||||
width: 0; |
|
||||||
height: 0; |
|
||||||
} |
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue