|
|
|
|
const app = getApp<IAppOption>()
|
|
|
|
|
|
|
|
|
|
interface PhotoItem {
|
|
|
|
|
photoId: string
|
|
|
|
|
photoAngle: string
|
|
|
|
|
photoUrl: string
|
|
|
|
|
checkStatus: number
|
|
|
|
|
isContinue: boolean
|
|
|
|
|
message: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface RecordPhoto {
|
|
|
|
|
photoId: string
|
|
|
|
|
photoAngle: string
|
|
|
|
|
photoAngleName: string
|
|
|
|
|
photoUrl: string
|
|
|
|
|
uploadTime: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface RecordDetail {
|
|
|
|
|
recordId: string
|
|
|
|
|
recordDate: string
|
|
|
|
|
treatmentCount: number
|
|
|
|
|
isBaseline: number
|
|
|
|
|
leftEye: number
|
|
|
|
|
rightEye: number
|
|
|
|
|
interorbitalDistance: number
|
|
|
|
|
uploadCompleted: number
|
|
|
|
|
photos: RecordPhoto[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Page({
|
|
|
|
|
data: {
|
|
|
|
|
popupShow: false,
|
|
|
|
|
popupType: 'popup16',
|
|
|
|
|
popupParams: {
|
|
|
|
|
close: false,
|
|
|
|
|
position: 'bottom',
|
|
|
|
|
} as any,
|
|
|
|
|
|
|
|
|
|
imagePreview: false,
|
|
|
|
|
previewImageSrc: '',
|
|
|
|
|
currentPhotoAngle: '',
|
|
|
|
|
|
|
|
|
|
recordId: '',
|
|
|
|
|
// 表单数据
|
|
|
|
|
recordDate: '',
|
|
|
|
|
treatmentCount: 0,
|
|
|
|
|
isBaseline: 0,
|
|
|
|
|
leftEye: '',
|
|
|
|
|
rightEye: '',
|
|
|
|
|
interorbitalDistance: '',
|
|
|
|
|
|
|
|
|
|
// 照片数据
|
|
|
|
|
photoMap: {} as Record<string, PhotoItem>,
|
|
|
|
|
|
|
|
|
|
// 顺序拍摄模式
|
|
|
|
|
sequentialShootMode: false,
|
|
|
|
|
sequentialShootList: [] as { name: string, type: string, angle: string }[],
|
|
|
|
|
sequentialShootIndex: 0,
|
|
|
|
|
|
|
|
|
|
// 替妥尤单抗使用次数选项 (0-9, 9表示大于8)
|
|
|
|
|
treatmentCountRange: ['0次', '1次', '2次', '3次', '4次', '5次', '6次', '7次', '8次', '大于8次'],
|
|
|
|
|
|
|
|
|
|
cameraList: {
|
|
|
|
|
frontend: [
|
|
|
|
|
{ name: '睁眼', type: 'front_open', angle: 'front_open' },
|
|
|
|
|
{ name: '闭眼', type: 'front_closed', angle: 'front_closed' },
|
|
|
|
|
{ name: '仰头', type: 'front_looking_up', angle: 'front_looking_up' },
|
|
|
|
|
],
|
|
|
|
|
backend: [
|
|
|
|
|
{ name: '左侧-90°', type: 'side_left_90', angle: 'side_left_90' },
|
|
|
|
|
{ name: '右侧-90°', type: 'side_right_90', angle: 'side_right_90' },
|
|
|
|
|
{ name: '左侧-45°', type: 'side_left_45', angle: 'side_left_45' },
|
|
|
|
|
{ name: '右侧-45°', type: 'side_right_45', angle: 'side_right_45' },
|
|
|
|
|
],
|
|
|
|
|
other: [
|
|
|
|
|
{ name: '左上', type: 'eye_up_left', angle: 'eye_up_left' },
|
|
|
|
|
{ name: '向上', type: 'eye_up', angle: 'eye_up' },
|
|
|
|
|
{ name: '右上', type: 'eye_up_right', angle: 'eye_up_right' },
|
|
|
|
|
{ name: '向左', type: 'eye_left', angle: 'eye_left' },
|
|
|
|
|
{ name: '向右', type: 'eye_right', angle: 'eye_right' },
|
|
|
|
|
{ name: '左下', type: 'eye_down_left', angle: 'eye_down_left' },
|
|
|
|
|
{ name: '向下', type: 'eye_down', angle: 'eye_down' },
|
|
|
|
|
{ name: '右下', type: 'eye_down_right', angle: 'eye_down_right' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onLoad(option: any) {
|
|
|
|
|
const recordId = option?.recordId || ''
|
|
|
|
|
// 新增时默认日期为今天;编辑时以接口返回为准
|
|
|
|
|
const today = new Date().toISOString().split('T')[0]
|
|
|
|
|
this.setData({
|
|
|
|
|
recordId,
|
|
|
|
|
recordDate: recordId ? '' : today,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onShow() {
|
|
|
|
|
app.waitLogin({ type: [1] }).then(() => {
|
|
|
|
|
if (this.data.recordId) {
|
|
|
|
|
this.getRecordDetail()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 获取记录详情(用于编辑/补拍回显)
|
|
|
|
|
getRecordDetail() {
|
|
|
|
|
wx.showLoading({ title: '加载中...' })
|
|
|
|
|
wx.ajax({
|
|
|
|
|
method: 'GET',
|
|
|
|
|
url: '?r=xd/proptosis/record-detail',
|
|
|
|
|
data: {
|
|
|
|
|
recordId: this.data.recordId,
|
|
|
|
|
},
|
|
|
|
|
}).then((res: RecordDetail) => {
|
|
|
|
|
wx.hideLoading()
|
|
|
|
|
const photoMap: Record<string, PhotoItem> = {}
|
|
|
|
|
;(res.photos || []).forEach((p) => {
|
|
|
|
|
photoMap[p.photoAngle] = {
|
|
|
|
|
photoId: p.photoId,
|
|
|
|
|
photoAngle: p.photoAngle,
|
|
|
|
|
photoUrl: p.photoUrl,
|
|
|
|
|
// 已在后台入库的历史照片,默认视为可继续
|
|
|
|
|
checkStatus: 1,
|
|
|
|
|
isContinue: true,
|
|
|
|
|
message: '',
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
recordDate: res.recordDate || '',
|
|
|
|
|
treatmentCount: typeof res.treatmentCount === 'number' ? res.treatmentCount : 0,
|
|
|
|
|
isBaseline: res.isBaseline || 0,
|
|
|
|
|
leftEye: res.leftEye != null ? String(res.leftEye) : '',
|
|
|
|
|
rightEye: res.rightEye != null ? String(res.rightEye) : '',
|
|
|
|
|
interorbitalDistance: res.interorbitalDistance != null ? String(res.interorbitalDistance) : '',
|
|
|
|
|
photoMap,
|
|
|
|
|
})
|
|
|
|
|
}).catch((err) => {
|
|
|
|
|
wx.hideLoading()
|
|
|
|
|
console.error('获取记录详情失败:', err)
|
|
|
|
|
wx.showToast({ title: '加载失败', icon: 'none' })
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 日期选择
|
|
|
|
|
onDateChange(e: any) {
|
|
|
|
|
this.setData({
|
|
|
|
|
recordDate: e.detail.value,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 替妥尤单抗使用次数选择
|
|
|
|
|
onTreatmentCountChange(e: any) {
|
|
|
|
|
const index = e.detail.value
|
|
|
|
|
this.setData({
|
|
|
|
|
treatmentCount: index,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 是否设置为基准照
|
|
|
|
|
onBaselineChange(e: any) {
|
|
|
|
|
const values: string[] = e?.detail?.value || []
|
|
|
|
|
this.setData({
|
|
|
|
|
isBaseline: values.length > 0 ? 1 : 0,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 左眼度数输入
|
|
|
|
|
onLeftEyeInput(e: any) {
|
|
|
|
|
this.setData({
|
|
|
|
|
leftEye: e.detail.value,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 右眼度数输入
|
|
|
|
|
onRightEyeInput(e: any) {
|
|
|
|
|
this.setData({
|
|
|
|
|
rightEye: e.detail.value,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 眶间距输入
|
|
|
|
|
onInterorbitalDistanceInput(e: any) {
|
|
|
|
|
this.setData({
|
|
|
|
|
interorbitalDistance: e.detail.value,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 打开相机选择照片
|
|
|
|
|
handleCamera(e: any) {
|
|
|
|
|
const { angle } = e.currentTarget.dataset
|
|
|
|
|
this.setData({
|
|
|
|
|
currentPhotoAngle: angle,
|
|
|
|
|
})
|
|
|
|
|
// 调用 camera 组件的 handleSelect 方法,传入类型
|
|
|
|
|
const cameraComponent = this.selectComponent('#camera-component')
|
|
|
|
|
if (cameraComponent) {
|
|
|
|
|
// 根据 angle 映射到对应的 type
|
|
|
|
|
const type = this.getCameraType(angle)
|
|
|
|
|
cameraComponent.handleSelect(type)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 一键顺序拍摄
|
|
|
|
|
handleSequentialShoot() {
|
|
|
|
|
// 定义拍摄顺序:正面 -> 侧面 -> 眼球运动
|
|
|
|
|
const shootOrder = [
|
|
|
|
|
...this.data.cameraList.frontend,
|
|
|
|
|
...this.data.cameraList.backend,
|
|
|
|
|
...this.data.cameraList.other,
|
|
|
|
|
]
|
|
|
|
|
this.setData({
|
|
|
|
|
sequentialShootMode: true,
|
|
|
|
|
sequentialShootList: shootOrder,
|
|
|
|
|
sequentialShootIndex: 0,
|
|
|
|
|
})
|
|
|
|
|
// 开始第一个拍摄
|
|
|
|
|
this.startSequentialShoot()
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 开始顺序拍摄
|
|
|
|
|
startSequentialShoot() {
|
|
|
|
|
const { sequentialShootList, sequentialShootIndex } = this.data
|
|
|
|
|
if (sequentialShootIndex >= sequentialShootList.length) {
|
|
|
|
|
// 全部拍摄完成
|
|
|
|
|
this.setData({
|
|
|
|
|
sequentialShootMode: false,
|
|
|
|
|
sequentialShootList: [],
|
|
|
|
|
sequentialShootIndex: 0,
|
|
|
|
|
})
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '全部拍摄完成',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const currentItem = sequentialShootList[sequentialShootIndex]
|
|
|
|
|
this.setData({
|
|
|
|
|
currentPhotoAngle: currentItem.angle,
|
|
|
|
|
})
|
|
|
|
|
// 调用 camera 组件,设置 onlyCamera 为 true
|
|
|
|
|
const cameraComponent = this.selectComponent('#camera-component')
|
|
|
|
|
if (cameraComponent) {
|
|
|
|
|
const type = this.getCameraType(currentItem.angle)
|
|
|
|
|
cameraComponent.setData({ onlyCamera: true })
|
|
|
|
|
cameraComponent.handleSelect(type)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 顺序拍摄模式下的上传成功回调
|
|
|
|
|
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
|
|
|
|
|
getCameraType(angle: string): number {
|
|
|
|
|
const typeMap: Record<string, number> = {
|
|
|
|
|
front_open: 1,
|
|
|
|
|
front_closed: 2,
|
|
|
|
|
front_looking_up: 3,
|
|
|
|
|
side_left_90: 4,
|
|
|
|
|
side_right_90: 5,
|
|
|
|
|
side_left_45: 6,
|
|
|
|
|
side_right_45: 7,
|
|
|
|
|
eye_up_left: 8,
|
|
|
|
|
eye_up: 9,
|
|
|
|
|
eye_up_right: 10,
|
|
|
|
|
eye_left: 11,
|
|
|
|
|
eye_right: 12,
|
|
|
|
|
eye_down_left: 13,
|
|
|
|
|
eye_down: 14,
|
|
|
|
|
eye_down_right: 15,
|
|
|
|
|
}
|
|
|
|
|
return typeMap[angle] || 1
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// camera 组件上传成功回调
|
|
|
|
|
onUploadSuccess(e: any) {
|
|
|
|
|
const { photoId, photoAngle, photoUrl, checkStatus, isContinue, message } = e.detail
|
|
|
|
|
|
|
|
|
|
// 不允许继续的情况:不写入 photoMap,提示重新上传;顺序拍摄不推进 index
|
|
|
|
|
if (checkStatus === 2 && !isContinue) {
|
|
|
|
|
wx.showToast({ title: message || '图片不合规,请重新上传', icon: 'none' })
|
|
|
|
|
if (this.data.sequentialShootMode) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.startSequentialShoot()
|
|
|
|
|
}, 500)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存照片信息到页面数据
|
|
|
|
|
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 })
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// camera 组件上传失败回调
|
|
|
|
|
onUploadError(e: any) {
|
|
|
|
|
const { errMsg } = e.detail
|
|
|
|
|
wx.showToast({ title: errMsg || '上传失败', icon: 'none' })
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 关闭相机
|
|
|
|
|
onCloseCamera() {
|
|
|
|
|
// camera 组件内部处理关闭逻辑
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 预览图片
|
|
|
|
|
handlePreview(e: any) {
|
|
|
|
|
const { angle } = e.currentTarget.dataset
|
|
|
|
|
const photo = this.data.photoMap[angle]
|
|
|
|
|
if (photo) {
|
|
|
|
|
this.setData({
|
|
|
|
|
imagePreview: true,
|
|
|
|
|
previewImageSrc: photo.photoUrl,
|
|
|
|
|
currentPhotoAngle: angle,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 重新拍摄
|
|
|
|
|
handleImageRetake() {
|
|
|
|
|
this.setData({
|
|
|
|
|
imagePreview: false,
|
|
|
|
|
})
|
|
|
|
|
this.handleCamera({ currentTarget: { dataset: { angle: this.data.currentPhotoAngle } } })
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 删除图片
|
|
|
|
|
handleImageDel() {
|
|
|
|
|
const photoMap = this.data.photoMap
|
|
|
|
|
delete photoMap[this.data.currentPhotoAngle]
|
|
|
|
|
this.setData({
|
|
|
|
|
imagePreview: false,
|
|
|
|
|
photoMap,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 保存记录
|
|
|
|
|
handleSave() {
|
|
|
|
|
const { recordId, recordDate, treatmentCount, isBaseline, leftEye, rightEye, interorbitalDistance, photoMap } = this.data
|
|
|
|
|
|
|
|
|
|
// 表单验证
|
|
|
|
|
if (!recordDate) {
|
|
|
|
|
wx.showToast({ title: '请选择记录日期', icon: 'none' })
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取所有已上传的照片ID
|
|
|
|
|
const photoIds = Object.values(photoMap).map(photo => photo.photoId).join(',')
|
|
|
|
|
|
|
|
|
|
const data: any = {
|
|
|
|
|
...(recordId ? { recordId } : {}),
|
|
|
|
|
recordDate,
|
|
|
|
|
treatmentCount,
|
|
|
|
|
isBaseline,
|
|
|
|
|
photoIds,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 可选字段
|
|
|
|
|
if (leftEye)
|
|
|
|
|
data.leftEye = leftEye
|
|
|
|
|
if (rightEye)
|
|
|
|
|
data.rightEye = rightEye
|
|
|
|
|
if (interorbitalDistance)
|
|
|
|
|
data.interorbitalDistance = interorbitalDistance
|
|
|
|
|
|
|
|
|
|
wx.showLoading({ title: '保存中...' })
|
|
|
|
|
|
|
|
|
|
wx.ajax({
|
|
|
|
|
method: 'POST',
|
|
|
|
|
url: '?r=xd/proptosis/record-save',
|
|
|
|
|
data,
|
|
|
|
|
}).then(() => {
|
|
|
|
|
wx.hideLoading()
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '保存成功',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
})
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
wx.navigateBack()
|
|
|
|
|
}, 1500)
|
|
|
|
|
}).catch((err: any) => {
|
|
|
|
|
wx.hideLoading()
|
|
|
|
|
const msg = err?.data?.msg || '保存失败'
|
|
|
|
|
wx.showToast({ title: msg, icon: 'none' })
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleDemo() {
|
|
|
|
|
wx.navigateTo({
|
|
|
|
|
url: '/patient/pages/noteDemo/index',
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handlePopupOk() {
|
|
|
|
|
this.setData({
|
|
|
|
|
popupShow: false,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handlePopupCancel() {
|
|
|
|
|
this.setData({
|
|
|
|
|
popupShow: false,
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
handleBack() {
|
|
|
|
|
wx.navigateBack()
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export {}
|