信达小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

547 lines
14 KiB

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: 'center',
} 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次'],
hasUnsavedData: false,
isCapturing: false,
hasBaseline: false,
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,
today,
})
},
onShow() {
if (this.data.isCapturing) {
this.setData({ isCapturing: false })
return
}
app.waitLogin({ type: [1] }).then(() => {
this.getBaselineStatus()
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,
})
},
// 获取基准照状态
getBaselineStatus() {
wx.ajax({
method: 'GET',
url: '?r=xd/proptosis/baseline-status',
data: {},
}).then((res: any) => {
this.setData({
hasBaseline: res.hasBaseline,
isBaseline: res.hasBaseline ? this.data.isBaseline : 1,
})
}).catch(() => {})
},
// 是否设置为基准照
onBaselineChange(e: any) {
const checked = e.detail.value.length > 0
if (checked && this.data.hasBaseline) {
this.setData({
popupShow: true,
popupType: 'popup18',
})
return
}
this.setData({
isBaseline: checked ? 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,
isCapturing: true,
})
// 调用 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,
isCapturing: true,
})
// 开始第一个拍摄
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,
hasUnsavedData: true,
})
// 延迟一下再打开下一个相机,给用户反馈时间
setTimeout(() => {
this.startSequentialShoot()
}, 500)
}
else {
this.setData({ photoMap, hasUnsavedData: true })
}
},
// 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,
})
}
},
// 关闭图片预览
handleImagePreviewClose() {
this.setData({
imagePreview: false,
})
},
// 重新拍摄
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,
hasUnsavedData: true,
})
},
// 保存记录
handleSave() {
const { recordId, recordDate, treatmentCount, isBaseline, leftEye, rightEye, interorbitalDistance, photoMap } = this.data
// 表单验证
if (!recordDate) {
wx.showToast({ title: '请选择记录日期', icon: 'none' })
return
}
// 获取所有已上传的照片
const photos = Object.values(photoMap)
if (photos.length === 0) {
wx.showToast({ title: '请至少上传一张照片', icon: 'none' })
return
}
// 所有照片全不合规才不允许保存
const hasCompliantPhoto = photos.some(photo => photo.checkStatus === 1)
if (!hasCompliantPhoto) {
wx.showToast({ title: '所有照片不合规,请重新上传', icon: 'none' })
return
}
const photoIds = photos.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()
this.setData({ hasUnsavedData: false })
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() {
const { popupType } = this.data
this.setData({
popupShow: false,
})
if (popupType === 'popup18') {
this.setData({ isBaseline: 1 })
}
else {
this.handleSave()
}
},
handlePopupCancel() {
const { popupType } = this.data
this.setData({
popupShow: false,
})
if (popupType === 'popup18') {
this.setData({ isBaseline: 0 })
}
else {
wx.navigateBack()
}
},
handleBack() {
if (this.data.hasUnsavedData) {
this.setData({
popupShow: true,
popupType: 'popup16',
})
}
else {
wx.navigateBack()
}
},
})
export {}