Browse Source

Improve diff baseline display and camera UX

- Show baseline date separately with icon badge in diff
  date selection
- Sort photos with baseline first in diff edit
- Add left/right orientation labels on camera guide
- Add tips for eye movement camera angles
- Stop sequential shooting when opening single angle
- Change note list title to patient-specific diary name
dev
kola-web 6 days ago
parent
commit
f2ad084a8f
  1. 9
      src/pages/d_noteDiff/index.scss
  2. 165
      src/pages/d_noteDiff/index.ts
  3. 4
      src/pages/d_noteDiff/index.wxml
  4. 96
      src/pages/d_noteDiffEdit/index.ts
  5. 2
      src/pages/d_noteList/index.wxml
  6. 14
      src/patient/components/camera/index.scss
  7. 18
      src/patient/components/camera/index.ts
  8. 2
      src/patient/components/camera/index.wxml
  9. 74
      src/patient/pages/noteAdd/index.ts
  10. 10
      src/patient/pages/noteDiff/index.scss
  11. 133
      src/patient/pages/noteDiff/index.ts
  12. 17
      src/patient/pages/noteDiff/index.wxml
  13. 74
      src/patient/pages/noteDiffEdit/index.ts

9
src/pages/d_noteDiff/index.scss

@ -80,6 +80,7 @@ page {
gap: 24rpx 22rpx; gap: 24rpx 22rpx;
.item { .item {
position: relative;
padding: 16rpx; padding: 16rpx;
font-size: 32rpx; font-size: 32rpx;
color: #211d2e; color: #211d2e;
@ -93,6 +94,14 @@ page {
color: #fff; color: #fff;
background: linear-gradient(180deg, #e98ff8 0%, #b073ff 100%); background: linear-gradient(180deg, #e98ff8 0%, #b073ff 100%);
} }
.icon {
position: absolute;
top: -18rpx;
right: 0;
width: 90rpx;
height: 52rpx;
}
} }
} }
} }

165
src/pages/d_noteDiff/index.ts

@ -27,7 +27,7 @@ Page({
// 对比角度 // 对比角度
photoAngle: '', photoAngle: '',
photoAngleName: '', photoAngleName: '',
angleList: [] as { key: string, name: string }[], angleList: [] as { key: string; name: string }[],
angleMap: {} as Record<string, string>, angleMap: {} as Record<string, string>,
// 日期选择 // 日期选择
@ -48,8 +48,7 @@ Page({
}) })
}, },
onShow() { onShow() {},
},
// 获取对比角度列表 // 获取对比角度列表
getCompareAngle() { getCompareAngle() {
@ -58,33 +57,34 @@ Page({
url: '?r=xd/doctor/proptosis/get-compare-angle', url: '?r=xd/doctor/proptosis/get-compare-angle',
// 兼容后端实际需要 patientId(文档可能漏写) // 兼容后端实际需要 patientId(文档可能漏写)
data: this.data.patientId ? { patientId: this.data.patientId } : {}, data: this.data.patientId ? { patientId: this.data.patientId } : {},
}).then((res: any) => {
const angleMap = res.photoAngle || {}
const angleList = Object.keys(angleMap).map(key => ({
key,
name: angleMap[key],
}))
this.setData({
angleMap,
angleList,
// 默认选择第一个角度
photoAngle: angleList[0]?.key || '',
photoAngleName: angleList[0]?.name || '',
})
// 获取对比日期
if (this.data.photoAngle) {
this.getCompareDates()
}
}).catch((err) => {
console.error('获取对比角度失败:', err)
wx.showToast({ title: '获取对比角度失败', icon: 'none' })
}) })
.then((res: any) => {
const angleMap = res.photoAngle || {}
const angleList = Object.keys(angleMap).map((key) => ({
key,
name: angleMap[key],
}))
this.setData({
angleMap,
angleList,
// 默认选择第一个角度
photoAngle: angleList[0]?.key || '',
photoAngleName: angleList[0]?.name || '',
})
// 获取对比日期
if (this.data.photoAngle) {
this.getCompareDates()
}
})
.catch((err) => {
console.error('获取对比角度失败:', err)
wx.showToast({ title: '获取对比角度失败', icon: 'none' })
})
}, },
// 获取对比可用日期 // 获取对比可用日期
getCompareDates() { getCompareDates() {
if (!this.data.photoAngle) if (!this.data.photoAngle) return
return
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '?r=xd/doctor/proptosis/compare-dates', url: '?r=xd/doctor/proptosis/compare-dates',
@ -92,15 +92,34 @@ Page({
patientId: this.data.patientId, patientId: this.data.patientId,
photoAngle: this.data.photoAngle, photoAngle: this.data.photoAngle,
}, },
}).then((res: any) => {
this.setData({
baseline: res.baseline || null,
nonBaselineList: res.nonBaselineList || [],
})
}).catch((err) => {
console.error('获取对比日期失败:', err)
wx.showToast({ title: '获取对比日期失败', icon: 'none' })
}) })
.then((res: any) => {
const newBaseline = res.baseline || null
const newList = res.nonBaselineList || []
const newRecordIds = newList.map((item: CompareDate) => item.recordId)
if (newBaseline) {
newRecordIds.push(newBaseline.recordId)
}
const selectedDates = this.data.selectedDates.filter((id: string) => newRecordIds.includes(id))
const nonBaselineList = newList.map((item: CompareDate & { isSelected?: boolean }) => ({
...item,
isSelected: selectedDates.includes(item.recordId),
}))
this.setData({
baseline: newBaseline,
nonBaselineList,
selectedDates,
})
if (selectedDates.length > 0 || newBaseline) {
this.getComparePhotos()
} else {
this.setData({ comparePhotos: [] })
}
})
.catch((err) => {
console.error('获取对比日期失败:', err)
wx.showToast({ title: '获取对比日期失败', icon: 'none' })
})
}, },
// 选择对比角度 // 选择对比角度
@ -110,7 +129,6 @@ Page({
this.setData({ this.setData({
photoAngle: angle.key, photoAngle: angle.key,
photoAngleName: angle.name, photoAngleName: angle.name,
selectedDates: [],
comparePhotos: [], comparePhotos: [],
}) })
this.getCompareDates() this.getCompareDates()
@ -123,8 +141,12 @@ Page({
const index = selectedDates.indexOf(recordId) const index = selectedDates.indexOf(recordId)
if (index > -1) { if (index > -1) {
selectedDates.splice(index, 1) selectedDates.splice(index, 1)
} } else {
else { const maxSelect = this.data.baseline ? 5 : 6
if (selectedDates.length >= maxSelect) {
wx.showToast({ title: '最多选择6张对比图', icon: 'none' })
return
}
selectedDates.push(recordId) selectedDates.push(recordId)
} }
// 更新列表中的选中状态 // 更新列表中的选中状态
@ -140,12 +162,15 @@ Page({
// 获取对比照片 // 获取对比照片
getComparePhotos() { getComparePhotos() {
const { photoAngle, selectedDates, baseline } = this.data const { photoAngle, selectedDates, baseline } = this.data
if (!photoAngle || selectedDates.length === 0) { if (!photoAngle) {
this.setData({ comparePhotos: [] }) this.setData({ comparePhotos: [] })
return return
} }
// 包含基准照ID
const recordIds = baseline ? [baseline.recordId, ...selectedDates] : selectedDates const recordIds = baseline ? [baseline.recordId, ...selectedDates] : selectedDates
if (recordIds.length === 0) {
this.setData({ comparePhotos: [] })
return
}
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '?r=xd/doctor/proptosis/compare', url: '?r=xd/doctor/proptosis/compare',
@ -154,41 +179,43 @@ Page({
photoAngle, photoAngle,
recordIds: recordIds.join(','), recordIds: recordIds.join(','),
}, },
}).then((res: any) => { })
const baselineRes = res.baseline .then((res: any) => {
const compareList = res.compareList || [] const baselineRes = res.baseline
const merged: ComparePhoto[] = [] const compareList = res.compareList || []
if (baselineRes) { const merged: ComparePhoto[] = []
merged.push({ if (baselineRes) {
recordId: baselineRes.recordId, merged.push({
recordDate: baselineRes.recordDate, recordId: baselineRes.recordId,
isBaseline: 1, recordDate: baselineRes.recordDate,
photoUrl: baselineRes.photoUrl, isBaseline: 1,
leftEye: baselineRes.leftEye, photoUrl: baselineRes.photoUrl,
rightEye: baselineRes.rightEye, leftEye: baselineRes.leftEye,
interorbitalDistance: baselineRes.interorbitalDistance, rightEye: baselineRes.rightEye,
treatmentCount: baselineRes.treatmentCount, interorbitalDistance: baselineRes.interorbitalDistance,
treatmentCount: baselineRes.treatmentCount,
})
}
compareList.forEach((item: any) => {
merged.push({
recordId: item.recordId,
recordDate: item.recordDate,
isBaseline: 0,
photoUrl: item.photoUrl,
leftEye: item.leftEye,
rightEye: item.rightEye,
interorbitalDistance: item.interorbitalDistance,
treatmentCount: item.treatmentCount,
})
}) })
} this.setData({
compareList.forEach((item: any) => { comparePhotos: merged.filter((item) => item.photoUrl).sort((a, b) => b.isBaseline - a.isBaseline),
merged.push({
recordId: item.recordId,
recordDate: item.recordDate,
isBaseline: 0,
photoUrl: item.photoUrl,
leftEye: item.leftEye,
rightEye: item.rightEye,
interorbitalDistance: item.interorbitalDistance,
treatmentCount: item.treatmentCount,
}) })
}) })
this.setData({ .catch((err) => {
comparePhotos: merged.filter(item => item.photoUrl), console.error('获取对比照片失败:', err)
wx.showToast({ title: '获取对比照片失败', icon: 'none' })
}) })
}).catch((err) => {
console.error('获取对比照片失败:', err)
wx.showToast({ title: '获取对比照片失败', icon: 'none' })
})
}, },
// 生成对比图 // 生成对比图

4
src/pages/d_noteDiff/index.wxml

@ -22,6 +22,10 @@
<view class="form-item"> <view class="form-item">
<view class="title">选择对比日期(可多选)</view> <view class="title">选择对比日期(可多选)</view>
<view class="multiple"> <view class="multiple">
<view class="item active baseline" wx:if="{{baseline}}">
{{baseline.recordDate}}
<image class="icon" src="{{imageUrl}}icon169.png?t={{Timestamp}}"></image>
</view>
<view <view
class="item {{item.isSelected ? 'active' : ''}}" class="item {{item.isSelected ? 'active' : ''}}"
wx:for="{{nonBaselineList}}" wx:for="{{nonBaselineList}}"

96
src/pages/d_noteDiffEdit/index.ts

@ -50,14 +50,16 @@ Page({
data: { data: {
patientId: this.data.patientId, patientId: this.data.patientId,
}, },
}).then((res: any) => {
const angleMap = res.photoAngle || {}
this.setData({
photoAngleName: angleMap[this.data.photoAngle] || '',
})
}).catch((err) => {
console.error('获取角度名称失败:', err)
}) })
.then((res: any) => {
const angleMap = res.photoAngle || {}
this.setData({
photoAngleName: angleMap[this.data.photoAngle] || '',
})
})
.catch((err) => {
console.error('获取角度名称失败:', err)
})
}, },
// 获取对比照片 // 获取对比照片
@ -77,43 +79,45 @@ Page({
photoAngle, photoAngle,
recordIds: recordIds.join(','), recordIds: recordIds.join(','),
}, },
}).then((res: any) => { })
const baselineRes = res.baseline .then((res: any) => {
const compareList = res.compareList || [] const baselineRes = res.baseline
const photos: PhotoItem[] = [] const compareList = res.compareList || []
if (baselineRes) { const photos: PhotoItem[] = []
photos.push({ if (baselineRes) {
photoId: baselineRes.recordId, photos.push({
recordId: baselineRes.recordId, photoId: baselineRes.recordId,
photoUrl: baselineRes.photoUrl, recordId: baselineRes.recordId,
recordDate: baselineRes.recordDate, photoUrl: baselineRes.photoUrl,
isBaseline: 1, recordDate: baselineRes.recordDate,
treatmentCount: baselineRes.treatmentCount, isBaseline: 1,
isCropped: false, treatmentCount: baselineRes.treatmentCount,
croppedUrl: '', isCropped: false,
croppedUrl: '',
})
}
compareList.forEach((item: any) => {
photos.push({
photoId: item.recordId,
recordId: item.recordId,
photoUrl: item.photoUrl,
recordDate: item.recordDate,
isBaseline: 0,
treatmentCount: item.treatmentCount,
isCropped: false,
croppedUrl: '',
})
}) })
} this.setData({
compareList.forEach((item: any) => { photos: photos.filter((item) => item.photoUrl).sort((a, b) => b.isBaseline - a.isBaseline),
photos.push({ loading: false,
photoId: item.recordId,
recordId: item.recordId,
photoUrl: item.photoUrl,
recordDate: item.recordDate,
isBaseline: 0,
treatmentCount: item.treatmentCount,
isCropped: false,
croppedUrl: '',
}) })
}) })
this.setData({ .catch((err) => {
photos: photos.filter(item => item.photoUrl), console.error('获取对比照片失败:', err)
loading: false, this.setData({ loading: false })
wx.showToast({ title: '获取照片失败', icon: 'none' })
}) })
}).catch((err) => {
console.error('获取对比照片失败:', err)
this.setData({ loading: false })
wx.showToast({ title: '获取照片失败', icon: 'none' })
})
}, },
// 点击裁剪 // 点击裁剪
@ -173,9 +177,10 @@ Page({
const mergeComponent = this.selectComponent('#merge') const mergeComponent = this.selectComponent('#merge')
if (mergeComponent) { if (mergeComponent) {
const imageList = photos.map((item) => { const imageList = photos.map((item) => {
const label = item.isBaseline === 1 const label =
? `基准照片 ${item.recordDate}` item.isBaseline === 1
: `对比照片 ${item.recordDate} 替妥尤单抗:${item.treatmentCount >= 9 ? '>8' : item.treatmentCount}` ? `基准照片 ${item.recordDate} 替妥尤单抗:${item.treatmentCount >= 9 ? '>8' : item.treatmentCount}`
: `对比照片 ${item.recordDate} 替妥尤单抗:${item.treatmentCount >= 9 ? '>8' : item.treatmentCount}`
return { return {
src: item.croppedUrl || item.photoUrl, src: item.croppedUrl || item.photoUrl,
time: label, time: label,
@ -220,15 +225,14 @@ Page({
// 是否有裁剪过的照片 // 是否有裁剪过的照片
hasCroppedPhoto(): boolean { hasCroppedPhoto(): boolean {
return this.data.photos.some(item => item.isCropped) return this.data.photos.some((item) => item.isCropped)
}, },
// 返回上一页 // 返回上一页
handleBack() { handleBack() {
if (this.hasCroppedPhoto()) { if (this.hasCroppedPhoto()) {
this.setData({ popupShow: true }) this.setData({ popupShow: true })
} } else {
else {
wx.navigateBack() wx.navigateBack()
} }
}, },

2
src/pages/d_noteList/index.wxml

@ -1,4 +1,4 @@
<navbar fixed title="记" custom-style="background:{{background}}"> <navbar fixed title="{{patientName}}的突眼日记" custom-style="background:{{background}}">
<van-icon name="arrow-left" slot="left" size="18px" color="#000" bind:tap="handleBack" /> <van-icon name="arrow-left" slot="left" size="18px" color="#000" bind:tap="handleBack" />
</navbar> </navbar>

14
src/patient/components/camera/index.scss

@ -70,6 +70,20 @@
width: 606rpx; width: 606rpx;
max-height: 304rpx; max-height: 304rpx;
} }
.left {
position: absolute;
top: 156rpx;
left: 28rpx;
font-size: 32rpx;
color: #ffffff;
}
.right {
position: absolute;
top: 156rpx;
right: 28rpx;
font-size: 32rpx;
color: #ffffff;
}
} }
&::before { &::before {
content: ''; content: '';

18
src/patient/components/camera/index.ts

@ -44,14 +44,14 @@ Component({
5: { name: '右侧-90°', group: '侧面', index: 2, total: 4, tip: '身体与头部完全转向左侧,呈标准90°侧面,仅可见右侧眼睛。' }, 5: { name: '右侧-90°', group: '侧面', index: 2, total: 4, tip: '身体与头部完全转向左侧,呈标准90°侧面,仅可见右侧眼睛。' },
6: { name: '左侧-45°', group: '侧面', index: 3, total: 4, tip: '身体与头部转向右前方45°。' }, 6: { name: '左侧-45°', group: '侧面', index: 3, total: 4, tip: '身体与头部转向右前方45°。' },
7: { name: '右侧-45°', group: '侧面', index: 4, total: 4, tip: '身体与头部转向左前方45°' }, 7: { name: '右侧-45°', group: '侧面', index: 4, total: 4, tip: '身体与头部转向左前方45°' },
8: { name: '左上', group: '眼球运动', index: 1, total: 8 }, 8: { name: '左上', group: '眼球运动', index: 1, total: 8, tip: '正对镜头,双眼向左上方看。' },
9: { name: '向上', group: '眼球运动', index: 2, total: 8 }, 9: { name: '向上', group: '眼球运动', index: 2, total: 8, tip: '正对镜头,双眼向上方看。' },
10: { name: '右上', group: '眼球运动', index: 3, total: 8 }, 10: { name: '右上', group: '眼球运动', index: 3, total: 8, tip: '正对镜头,双眼向右上方看。' },
11: { name: '向左', group: '眼球运动', index: 4, total: 8 }, 11: { name: '向左', group: '眼球运动', index: 4, total: 8, tip: '正对镜头,双眼向左方看。' },
12: { name: '向右', group: '眼球运动', index: 5, total: 8 }, 12: { name: '向右', group: '眼球运动', index: 5, total: 8, tip: '正对镜头,双眼向右方看。' },
13: { name: '左下', group: '眼球运动', index: 6, total: 8 }, 13: { name: '左下', group: '眼球运动', index: 6, total: 8, tip: '正对镜头,双眼向左下方看。' },
14: { name: '向下', group: '眼球运动', index: 7, total: 8 }, 14: { name: '向下', group: '眼球运动', index: 7, total: 8, tip: '正对镜头,双眼向下方看。' },
15: { name: '右下', group: '眼球运动', index: 8, total: 8 }, 15: { name: '右下', group: '眼球运动', index: 8, total: 8, tip: '正对镜头,双眼向右下方看。' },
}, },
frame: { frame: {
1: { 1: {
@ -155,6 +155,7 @@ Component({
}) })
}, },
handleHideCamera() { handleHideCamera() {
this.triggerEvent('close')
if (this.properties.onlyCamera) { if (this.properties.onlyCamera) {
this.setData({ this.setData({
visible: false, visible: false,
@ -410,6 +411,7 @@ Component({
// 关闭相机 // 关闭相机
this.setData({ this.setData({
visible: false, visible: false,
selectShow: false,
}) })
// 如果机审不通过且不允许继续,提示用户 // 如果机审不通过且不允许继续,提示用户

2
src/patient/components/camera/index.wxml

@ -15,6 +15,8 @@
<view class="photo-wrap"> <view class="photo-wrap">
<image class="photo" src="{{imageUrl}}{{frame[type].exampleSrc}}.png?t={{Timestamp}}"></image> <image class="photo" src="{{imageUrl}}{{frame[type].exampleSrc}}.png?t={{Timestamp}}"></image>
<image class="label" src="{{imageUrl}}icon162.png?t={{Timestamp}}"></image> <image class="label" src="{{imageUrl}}icon162.png?t={{Timestamp}}"></image>
<view class="right">左</view>
<view class="left">右</view>
</view> </view>
</view> </view>
</view> </view>

74
src/patient/pages/noteAdd/index.ts

@ -231,9 +231,11 @@ Page({
// 打开相机选择照片 // 打开相机选择照片
handleCamera(e: any) { handleCamera(e: any) {
const { angle } = e.currentTarget.dataset const { angle } = e.currentTarget.dataset
this._stopSequentialShoot()
this.setData({ this.setData({
currentPhotoAngle: angle, currentPhotoAngle: angle,
isCapturing: true, isCapturing: true,
sequentialShootMode: false,
}) })
const cameraComponent = this.selectComponent('#camera-component') const cameraComponent = this.selectComponent('#camera-component')
if (cameraComponent) { if (cameraComponent) {
@ -243,6 +245,18 @@ Page({
} }
}, },
_stopSequentialShoot() {
if (this._sequentialTimer) {
clearTimeout(this._sequentialTimer)
this._sequentialTimer = null
}
this.setData({
sequentialShootMode: false,
sequentialShootList: [],
sequentialShootIndex: 0,
})
},
// 一键顺序拍摄 // 一键顺序拍摄
handleSequentialShoot() { handleSequentialShoot() {
// 定义拍摄顺序:正面 -> 侧面 -> 眼球运动 // 定义拍摄顺序:正面 -> 侧面 -> 眼球运动
@ -290,39 +304,6 @@ Page({
} }
}, },
// 顺序拍摄模式下的上传成功回调
onSequentialUploadSuccess(e: any) {
const { photoId, photoAngle, photoUrl, checkStatus, isContinue, message } = e.detail
// 保存照片信息到页面数据
const photoMap = this.data.photoMap
photoMap[photoAngle] = {
photoId,
photoAngle,
photoUrl,
checkStatus,
isContinue,
message,
}
// 检查是否是顺序拍摄模式
if (this.data.sequentialShootMode) {
// 继续下一个拍摄
const nextIndex = this.data.sequentialShootIndex + 1
this.setData({
photoMap,
sequentialShootIndex: nextIndex,
})
// 延迟一下再打开下一个相机,给用户反馈时间
setTimeout(() => {
this.startSequentialShoot()
}, 500)
}
else {
this.setData({ photoMap })
}
},
// 将 angle 映射到 camera 组件的 type // 将 angle 映射到 camera 组件的 type
getCameraType(angle: string): number { getCameraType(angle: string): number {
const typeMap: Record<string, number> = { const typeMap: Record<string, number> = {
@ -349,14 +330,9 @@ Page({
onUploadSuccess(e: any) { onUploadSuccess(e: any) {
const { photoId, photoAngle, photoUrl, checkStatus, isContinue, message } = e.detail const { photoId, photoAngle, photoUrl, checkStatus, isContinue, message } = e.detail
// 不允许继续的情况:不写入 photoMap,提示重新上传;顺序拍摄不推进 index // 不允许继续的情况:不写入 photoMap,提示重新上传
if (checkStatus === 2 && !isContinue) { if (checkStatus === 2 && !isContinue) {
wx.showToast({ title: message || '图片不合规,请重新上传', icon: 'none' }) wx.showToast({ title: message || '图片不合规,请重新上传', icon: 'none' })
if (this.data.sequentialShootMode) {
setTimeout(() => {
this.startSequentialShoot()
}, 500)
}
return return
} }
@ -370,24 +346,16 @@ Page({
isContinue, isContinue,
message, message,
} }
this.setData({ photoMap, hasUnsavedData: true })
// 检查是否是顺序拍摄模式 // 顺序拍摄模式:继续下一个
if (this.data.sequentialShootMode) { if (this.data.sequentialShootMode) {
// 继续下一个拍摄
const nextIndex = this.data.sequentialShootIndex + 1 const nextIndex = this.data.sequentialShootIndex + 1
this.setData({ this.setData({ sequentialShootIndex: nextIndex })
photoMap, this._sequentialTimer = setTimeout(() => {
sequentialShootIndex: nextIndex,
hasUnsavedData: true,
})
// 延迟一下再打开下一个相机,给用户反馈时间
setTimeout(() => {
this.startSequentialShoot() this.startSequentialShoot()
}, 500) }, 500)
} }
else {
this.setData({ photoMap, hasUnsavedData: true })
}
}, },
// camera 组件上传失败回调 // camera 组件上传失败回调
@ -398,7 +366,9 @@ Page({
// 关闭相机 // 关闭相机
onCloseCamera() { onCloseCamera() {
// camera 组件内部处理关闭逻辑 if (this.data.sequentialShootMode) {
this._stopSequentialShoot()
}
}, },
// 预览图片 // 预览图片

10
src/patient/pages/noteDiff/index.scss

@ -101,6 +101,7 @@ page {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 24rpx 22rpx; gap: 24rpx 22rpx;
.item { .item {
position: relative;
padding: 16rpx; padding: 16rpx;
font-size: 32rpx; font-size: 32rpx;
color: #211d2e; color: #211d2e;
@ -113,6 +114,13 @@ page {
color: #fff; color: #fff;
background: linear-gradient(180deg, #e98ff8 0%, #b073ff 100%); background: linear-gradient(180deg, #e98ff8 0%, #b073ff 100%);
} }
.icon {
position: absolute;
top: -18rpx;
right: 0;
width: 90rpx;
height: 52rpx;
}
} }
} }
} }
@ -252,7 +260,7 @@ page {
align-items: baseline; align-items: baseline;
gap: 8rpx; gap: 8rpx;
.num { .num {
font-size: 56rpx; font-size: 32rpx;
color: #b073ff; color: #b073ff;
font-weight: bold; font-weight: bold;
} }

133
src/patient/pages/noteDiff/index.ts

@ -22,7 +22,7 @@ Page({
// 对比角度 // 对比角度
photoAngle: '', photoAngle: '',
photoAngleName: '', photoAngleName: '',
angleList: [] as { key: string, name: string }[], angleList: [] as { key: string; name: string }[],
angleMap: {} as Record<string, string>, angleMap: {} as Record<string, string>,
// 日期选择 // 日期选择
@ -53,26 +53,28 @@ Page({
method: 'GET', method: 'GET',
url: '?r=xd/proptosis/get-compare-angle', url: '?r=xd/proptosis/get-compare-angle',
data: {}, data: {},
}).then((res: any) => {
const angleMap = res.photoAngle || {}
const angleList = Object.keys(angleMap).map(key => ({
key,
name: angleMap[key],
}))
this.setData({
angleMap,
angleList,
// 默认选择第一个角度
photoAngle: angleList[0]?.key || '',
photoAngleName: angleList[0]?.name || '',
})
// 获取对比日期
if (this.data.photoAngle) {
this.getCompareDates()
}
}).catch((err) => {
console.error('获取对比角度失败:', err)
}) })
.then((res: any) => {
const angleMap = res.photoAngle || {}
const angleList = Object.keys(angleMap).map((key) => ({
key,
name: angleMap[key],
}))
this.setData({
angleMap,
angleList,
// 默认选择第一个角度
photoAngle: angleList[0]?.key || '',
photoAngleName: angleList[0]?.name || '',
})
// 获取对比日期
if (this.data.photoAngle) {
this.getCompareDates()
}
})
.catch((err) => {
console.error('获取对比角度失败:', err)
})
}, },
// 获取基准照状态 // 获取基准照状态
@ -81,38 +83,53 @@ Page({
method: 'GET', method: 'GET',
url: '?r=xd/proptosis/baseline-status', url: '?r=xd/proptosis/baseline-status',
data: {}, data: {},
}).then((res: any) => {
this.setData({
hasBaseline: res.hasBaseline,
})
}).catch((err) => {
console.error('获取基准照状态失败:', err)
}) })
.then((res: any) => {
this.setData({
hasBaseline: res.hasBaseline,
})
})
.catch((err) => {
console.error('获取基准照状态失败:', err)
})
}, },
// 获取对比可用日期 // 获取对比可用日期
getCompareDates() { getCompareDates() {
if (!this.data.photoAngle) if (!this.data.photoAngle) return
return
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '?r=xd/proptosis/compare-dates', url: '?r=xd/proptosis/compare-dates',
data: { data: {
photoAngle: this.data.photoAngle, photoAngle: this.data.photoAngle,
}, },
}).then((res: any) => {
const selectedDates = this.data.selectedDates
const nonBaselineList = (res.nonBaselineList || []).map((item: CompareDate) => ({
...item,
isSelected: selectedDates.includes(item.recordId),
}))
this.setData({
baseline: res.baseline || null,
nonBaselineList,
})
}).catch((err) => {
console.error('获取对比日期失败:', err)
}) })
.then((res: any) => {
const newBaseline = res.baseline || null
const newList = res.nonBaselineList || []
const newRecordIds = newList.map((item: CompareDate) => item.recordId)
if (newBaseline) {
newRecordIds.push(newBaseline.recordId)
}
const selectedDates = this.data.selectedDates.filter((id: string) => newRecordIds.includes(id))
const nonBaselineList = newList.map((item: CompareDate) => ({
...item,
isSelected: selectedDates.includes(item.recordId),
}))
this.setData({
baseline: newBaseline,
nonBaselineList,
selectedDates,
})
if (selectedDates.length > 0 || newBaseline) {
this.getComparePhotos()
} else {
this.setData({ comparePhotos: [] })
}
})
.catch((err) => {
console.error('获取对比日期失败:', err)
})
}, },
// 选择对比角度 // 选择对比角度
@ -122,7 +139,6 @@ Page({
this.setData({ this.setData({
photoAngle: angle.key, photoAngle: angle.key,
photoAngleName: angle.name, photoAngleName: angle.name,
selectedDates: [],
comparePhotos: [], comparePhotos: [],
}) })
this.getCompareDates() this.getCompareDates()
@ -135,8 +151,12 @@ Page({
const index = selectedDates.indexOf(recordId) const index = selectedDates.indexOf(recordId)
if (index > -1) { if (index > -1) {
selectedDates.splice(index, 1) selectedDates.splice(index, 1)
} } else {
else { const maxSelect = this.data.baseline ? 5 : 6
if (selectedDates.length >= maxSelect) {
wx.showToast({ title: '最多选择6张对比图', icon: 'none' })
return
}
selectedDates.push(recordId) selectedDates.push(recordId)
} }
// 更新列表中的选中状态 // 更新列表中的选中状态
@ -152,12 +172,15 @@ Page({
// 获取对比照片 // 获取对比照片
getComparePhotos() { getComparePhotos() {
const { photoAngle, selectedDates, baseline } = this.data const { photoAngle, selectedDates, baseline } = this.data
if (!photoAngle || selectedDates.length === 0) { if (!photoAngle) {
this.setData({ comparePhotos: [] }) this.setData({ comparePhotos: [] })
return return
} }
// 包含基准照ID
const recordIds = baseline ? [baseline.recordId, ...selectedDates] : selectedDates const recordIds = baseline ? [baseline.recordId, ...selectedDates] : selectedDates
if (recordIds.length === 0) {
this.setData({ comparePhotos: [] })
return
}
wx.ajax({ wx.ajax({
method: 'GET', method: 'GET',
url: '?r=xd/proptosis/compare-photos', url: '?r=xd/proptosis/compare-photos',
@ -165,15 +188,19 @@ Page({
photoAngle, photoAngle,
recordIds: recordIds.join(','), recordIds: recordIds.join(','),
}, },
}).then((res: any) => {
// 过滤掉 photoUrl 为空的数据
const photos = (res.photos || []).filter((item: any) => item.photoUrl)
this.setData({
comparePhotos: photos,
})
}).catch((err) => {
console.error('获取对比照片失败:', err)
}) })
.then((res: any) => {
// 过滤掉 photoUrl 为空的数据,确保基准照片排在第一位
const photos = (res.photos || [])
.filter((item: any) => item.photoUrl)
.sort((a: any, b: any) => b.isBaseline - a.isBaseline)
this.setData({
comparePhotos: photos,
})
})
.catch((err) => {
console.error('获取对比照片失败:', err)
})
}, },
// 去设置基准照 // 去设置基准照

17
src/patient/pages/noteDiff/index.wxml

@ -32,13 +32,19 @@
<view class="form-item"> <view class="form-item">
<view class="title">选择对比日期(可多选)</view> <view class="title">选择对比日期(可多选)</view>
<view class="multiple"> <view class="multiple">
<view class="item active baseline" wx:if="{{baseline}}">
{{baseline.recordDate}}
<image class="icon" src="{{imageUrl}}icon169.png?t={{Timestamp}}"></image>
</view>
<view <view
class="item {{item.isSelected ? 'active' : ''}}" class="item {{item.isSelected ? 'active' : ''}}"
wx:for="{{nonBaselineList}}" wx:for="{{nonBaselineList}}"
wx:key="recordId" wx:key="recordId"
data-record-id="{{item.recordId}}" data-record-id="{{item.recordId}}"
bind:tap="onDateSelect" bind:tap="onDateSelect"
>{{item.recordDate}}</view> >
{{item.recordDate}}
</view>
</view> </view>
</view> </view>
</view> </view>
@ -58,11 +64,16 @@
</view> </view>
<view class="tags"> <view class="tags">
<view wx:if="{{item.isBaseline === 1}}" class="tag tag1">基准照片</view> <view wx:if="{{item.isBaseline === 1}}" class="tag tag1">基准照片</view>
<view class="tag tag2" wx:if="{{item.treatmentCount > 0}}">替妥尤单抗:{{item.treatmentCount >= 9 ? '>8' : item.treatmentCount}}</view> <view class="tag tag2" wx:if="{{item.treatmentCount > 0}}">
替妥尤单抗:{{item.treatmentCount >= 9 ? '>8' : item.treatmentCount}}
</view>
</view> </view>
<view class="photo-card"> <view class="photo-card">
<image class="photo" src="{{item.photoUrl}}" mode="aspectFill"></image> <image class="photo" src="{{item.photoUrl}}" mode="aspectFill"></image>
<view class="row" wx:if="{{item.leftEye != null || item.rightEye != null || item.interorbitalDistance != null}}"> <view
class="row"
wx:if="{{item.leftEye != null || item.rightEye != null || item.interorbitalDistance != null}}"
>
<view class="col"> <view class="col">
<view class="name">右眼</view> <view class="name">右眼</view>
<view class="content"> <view class="content">

74
src/patient/pages/noteDiffEdit/index.ts

@ -48,14 +48,16 @@ Page({
method: 'GET', method: 'GET',
url: '?r=xd/proptosis/get-compare-angle', url: '?r=xd/proptosis/get-compare-angle',
data: {}, data: {},
}).then((res: any) => {
const angleMap = res.photoAngle || {}
this.setData({
photoAngleName: angleMap[this.data.photoAngle] || '',
})
}).catch((err) => {
console.error('获取角度名称失败:', err)
}) })
.then((res: any) => {
const angleMap = res.photoAngle || {}
this.setData({
photoAngleName: angleMap[this.data.photoAngle] || '',
})
})
.catch((err) => {
console.error('获取角度名称失败:', err)
})
}, },
// 获取对比照片 // 获取对比照片
@ -74,28 +76,32 @@ Page({
photoAngle, photoAngle,
recordIds: recordIds.join(','), recordIds: recordIds.join(','),
}, },
}).then((res: any) => {
const photos = (res.photos || []).map((item: any) => ({
photoId: item.photoId || item.recordId,
photoUrl: item.photoUrl,
recordDate: item.recordDate,
isBaseline: item.isBaseline,
treatmentCount: item.treatmentCount,
leftEye: item.leftEye,
rightEye: item.rightEye,
interorbitalDistance: item.interorbitalDistance,
isCropped: false,
croppedUrl: '',
}))
this.setData({
photos,
loading: false,
})
}).catch((err) => {
console.error('获取对比照片失败:', err)
this.setData({ loading: false })
wx.showToast({ title: '获取照片失败', icon: 'none' })
}) })
.then((res: any) => {
const photos = (res.photos || [])
.map((item: any) => ({
photoId: item.photoId || item.recordId,
photoUrl: item.photoUrl,
recordDate: item.recordDate,
isBaseline: item.isBaseline,
treatmentCount: item.treatmentCount,
leftEye: item.leftEye,
rightEye: item.rightEye,
interorbitalDistance: item.interorbitalDistance,
isCropped: false,
croppedUrl: '',
}))
.sort((a: PhotoItem, b: PhotoItem) => b.isBaseline - a.isBaseline)
this.setData({
photos,
loading: false,
})
})
.catch((err) => {
console.error('获取对比照片失败:', err)
this.setData({ loading: false })
wx.showToast({ title: '获取照片失败', icon: 'none' })
})
}, },
// 点击裁剪 // 点击裁剪
@ -155,9 +161,10 @@ Page({
const mergeComponent = this.selectComponent('#merge') const mergeComponent = this.selectComponent('#merge')
if (mergeComponent) { if (mergeComponent) {
const imageList = photos.map((item) => { const imageList = photos.map((item) => {
const label = item.isBaseline === 1 const label =
? `基准照片 ${item.recordDate}` item.isBaseline === 1
: `对比照片 ${item.recordDate} 替妥尤单抗:${item.treatmentCount >= 9 ? '>8' : item.treatmentCount}` ? `基准照片 ${item.recordDate} 替妥尤单抗:${item.treatmentCount >= 9 ? '>8' : item.treatmentCount}`
: `对比照片 ${item.recordDate} 替妥尤单抗:${item.treatmentCount >= 9 ? '>8' : item.treatmentCount}`
return { return {
src: item.croppedUrl || item.photoUrl, src: item.croppedUrl || item.photoUrl,
time: label, time: label,
@ -207,15 +214,14 @@ Page({
// 是否有裁剪过的照片 // 是否有裁剪过的照片
hasCroppedPhoto(): boolean { hasCroppedPhoto(): boolean {
return this.data.photos.some(item => item.isCropped) return this.data.photos.some((item) => item.isCropped)
}, },
// 返回上一页 // 返回上一页
handleBack() { handleBack() {
if (this.hasCroppedPhoto()) { if (this.hasCroppedPhoto()) {
this.setData({ popupShow: true }) this.setData({ popupShow: true })
} } else {
else {
wx.navigateBack() wx.navigateBack()
} }
}, },

Loading…
Cancel
Save