wlzboy
2026-01-26 739d4c2f64fcfd4ddcce6292978ad6aeb2c7bdc7
app/pagesTask/create-emergency.vue
@@ -400,41 +400,6 @@
        </view>
      </view>
    </uni-popup>
    <!-- 拍照识别弹窗 -->
    <uni-popup ref="photoOCRPopup" type="bottom" :safe-area="true">
      <view class="photo-ocr-popup">
        <view class="popup-header">
          <view class="popup-title">拍照识别</view>
          <view class="popup-close" @click="closePhotoOCRPopup">
            <uni-icons type="closeempty" size="24" color="#333"></uni-icons>
          </view>
        </view>
        <view class="ocr-content">
          <view class="ocr-tip">
            <uni-icons type="info" size="18" color="#007AFF"></uni-icons>
            <text>拍照或选择图片,自动识别转运单信息</text>
          </view>
          <view class="image-preview" v-if="ocrImage">
            <image :src="ocrImage" mode="aspectFit" style="width: 100%; height: 300rpx; border-radius: 10rpx;"></image>
          </view>
          <view class="ocr-actions">
            <button class="select-btn" @click="selectImage">选择图片</button>
            <button class="capture-btn" @click="captureImage">拍照</button>
          </view>
        </view>
        <view class="popup-footer">
          <button class="cancel-btn" @click="closePhotoOCRPopup">取消</button>
          <button class="confirm-btn" @click="performOCR" :disabled="ocrLoading">
            {{ ocrLoading ? '识别中...' : '开始识别' }}
          </button>
        </view>
      </view>
    </uni-popup>
  </scroll-view>
</template>
@@ -537,9 +502,6 @@
      // 智能识别相关
      rawText: '',
      parseLoading: false,
      // 拍照识别相关
      ocrImage: '',
      ocrLoading: false,
      // 多图片拍照识别相关
      multiOcrImages: [],
      multiOcrLoading: false,
@@ -1692,12 +1654,6 @@
    
    // ==================== 拍照识别相关方法 ====================
    
    // 显示拍照识别弹窗
    showPhotoOCRPopup() {
      this.ocrImage = ''
      this.$refs.photoOCRPopup.open()
    },
    // 显示多图拍照识别弹窗
    showMultiPhotoOCRPopup() {
      this.page1Image = ''
@@ -1774,38 +1730,6 @@
        fail: (err) => {
          console.error('选择图片失败:', err)
          this.$modal.showToast('选择图片失败')
        }
      })
    },
    // 选择图片
    selectImage() {
      uni.chooseImage({
        count: 1,
        sizeType: ['compressed'],
        sourceType: ['album'],
        success: (res) => {
          this.ocrImage = res.tempFilePaths[0]
        },
        fail: (err) => {
          console.error('选择图片失败:', err)
          this.$modal.showToast('选择图片失败')
        }
      })
    },
    // 拍照
    captureImage() {
      uni.chooseImage({
        count: 1,
        sizeType: ['compressed'],
        sourceType: ['camera'],
        success: (res) => {
          this.ocrImage = res.tempFilePaths[0]
        },
        fail: (err) => {
          console.error('拍照失败:', err)
          this.$modal.showToast('拍照失败')
        }
      })
    },
@@ -1963,41 +1887,101 @@
        }
      }
      
      // 提取诊断(病情)
      // 提取诊断(病情)(限制10个字符以内,避免误识别)
      if (fields['诊断']) {
        this.taskForm.patient.condition = fields['诊断'].trim()
        const diagnosis = fields['诊断'].trim()
        // 只有当10个字符以内的诊断信息才填入表单
        if (diagnosis.length <= 10) {
          this.taskForm.patient.condition = diagnosis
        } else {
          console.log('诊断信息过长,可能为误识别,已忽略:', diagnosis)
        }
      }
      
      // 提取行程(转出医院和转入医院)
      if (fields['行程']) {
        const route = fields['行程'].trim()
        // 按"-"或"—"分割行程
        const hospitals = route.split(/[-—]/).map(h => h.trim())
        console.log('【行程】OCR识别的行程信息:', route)
        // 按多种分隔符分割行程:-、—、---、-->、→、空格等
        const hospitals = route.split(/[-—]{1,3}|-->|→|\s{2,}/).map(h => h.trim()).filter(h => h.length > 0)
        console.log('【行程】分割后的医院列表:', hospitals)
        if (hospitals.length >= 2) {
          // 第一个是转出医院
          this.taskForm.hospitalOut.name = hospitals[0]
          // 第二个是转入医院
          this.taskForm.hospitalIn.name = hospitals[1]
          const outHospital = hospitals[0]
          this.taskForm.hospitalOut.name = outHospital
          
          // 尝试从医院库中匹配并补全地址
          // 如枟转出医院是“家中”,提取后面的详细地址
          if (outHospital === '家中' && hospitals.length >= 3) {
            // 将剩余部分作为详细地址
            const detailAddress = hospitals.slice(1, hospitals.length - 1).join(' ')
            if (detailAddress) {
              this.taskForm.hospitalOut.address = detailAddress
              console.log('【行程】转出家中地址:', detailAddress)
            }
            // 最后一个是转入医院
            this.taskForm.hospitalIn.name = hospitals[hospitals.length - 1]
            console.log('【行程】转入医院:', hospitals[hospitals.length - 1])
          } else {
            // 第二个是转入医院
            const inHospital = hospitals[1]
            this.taskForm.hospitalIn.name = inHospital
            // 如果转入医院是“家中”,提取后面的详细地址
            if (inHospital === '家中' && hospitals.length >= 3) {
              // 将剩余部分作为详细地址
              const detailAddress = hospitals.slice(2).join(' ')
              if (detailAddress) {
                this.taskForm.hospitalIn.address = detailAddress
                console.log('【行程】转入家中地址:', detailAddress)
              }
            }
          }
          // 尝试从医院库中匹配并补全地址(只在非“家中”时)
          const outHospitalName = this.taskForm.hospitalOut.name
          const inHospitalName = this.taskForm.hospitalIn.name
          Promise.all([
            this.findHospitalByName(hospitals[0], 'out', false),
            this.findHospitalByName(hospitals[1], 'in', false)
            outHospitalName !== '家中' ? this.findHospitalByName(outHospitalName, 'out', false) : Promise.resolve(null),
            inHospitalName !== '家中' ? this.findHospitalByName(inHospitalName, 'in', false) : Promise.resolve(null)
          ]).then(([outHosp, inHosp]) => {
            // 处理转出医院
            if (outHospitalName !== '家中') {
            if (outHosp) {
                // 找到医院,使用医院库中的信息
              this.taskForm.hospitalOut.id = outHosp.hospId
              this.taskForm.hospitalOut.name = outHosp.hospName
              if (outHosp.hospName !== '家中') {
                this.taskForm.hospitalOut.address = this.buildFullAddress(outHosp)
                this.taskForm.hospitalOut.city = outHosp.hopsCity || ''
                console.log('【行程】转出医院匹配成功:', outHosp.hospName)
              } else {
                // 找不到医院,默认设置为"家中",原医院名称作为地址
                console.log('【行程】转出医院未找到,默认设置为家中,地址为:', outHospitalName)
                this.taskForm.hospitalOut.name = '家中'
                this.taskForm.hospitalOut.address = outHospitalName
                this.taskForm.hospitalOut.id = null
                this.taskForm.hospitalOut.city = ''
              }
            }
            // 处理转入医院
            if (inHospitalName !== '家中') {
            if (inHosp) {
                // 找到医院,使用医院库中的信息
              this.taskForm.hospitalIn.id = inHosp.hospId
              this.taskForm.hospitalIn.name = inHosp.hospName
              if (inHosp.hospName !== '家中') {
                this.taskForm.hospitalIn.address = this.buildFullAddress(inHosp)
                this.taskForm.hospitalIn.city = inHosp.hopsCity || ''
                console.log('【行程】转入医院匹配成功:', inHosp.hospName)
              } else {
                // 找不到医院,默认设置为"家中",原医院名称作为地址
                console.log('【行程】转入医院未找到,默认设置为家中,地址为:', inHospitalName)
                this.taskForm.hospitalIn.name = '家中'
                this.taskForm.hospitalIn.address = inHospitalName
                this.taskForm.hospitalIn.id = null
                this.taskForm.hospitalIn.city = ''
              }
            }
            
@@ -2013,137 +1997,24 @@
      console.log('多图OCR结果处理完成,表单数据更新')
    },
    
    // 执行OCR识别
    performOCR() {
      if (!this.ocrImage) {
        this.$modal.showToast('请先选择或拍摄图片')
        return
      }
      this.ocrLoading = true
      // 使用OCR API进行识别
      recognizeImage(this.ocrImage, 'HandWriting', 'tencent', DEFAULT_TRANSFER_ITEM_NAMES)
        .then(response => {
          const ocrResult = response.data.ocrResult
          this.processOCRResult(ocrResult)
          this.$modal.showToast('OCR识别成功')
        })
        .catch(error => {
          console.error('OCR识别失败:', error)
          this.$modal.showToast(`OCR识别失败: ${error.msg || '未知错误'}`)
        })
        .finally(() => {
          this.ocrLoading = false
          this.closePhotoOCRPopup()
        })
    },
    // 处理OCR识别结果
    processOCRResult(ocrResult) {
      if (!ocrResult || !ocrResult.content) {
        console.log('OCR识别结果为空')
        return
      }
      const content = ocrResult.content
      console.log('OCR识别内容:', content)
      // 提取患者姓名
      const patientNameMatch = content.match(/患者姓名[::]?\s*([^\n,,。;;]+)/)
      if (patientNameMatch && patientNameMatch[1]) {
        this.taskForm.patient.name = patientNameMatch[1].trim()
      }
      // 提取性别
      const genderMatch = content.match(/性别[::]?\s*([^\n,,。;;]+)/)
      if (genderMatch && genderMatch[1]) {
        const gender = genderMatch[1].trim()
        if (gender.includes('男')) {
          this.taskForm.patient.gender = 'male'
        } else if (gender.includes('女')) {
          this.taskForm.patient.gender = 'female'
        }
      }
      // 提取身份证号
      const idCardMatch = content.match(/身份证号[::]?\s*([^\n,,。;;]+)/)
      const signerIdMatch = content.match(/签字人身份证号码[::]?\s*([^\n,,。;;]+)/)
      if (idCardMatch && idCardMatch[1]) {
        this.taskForm.patient.idCard = idCardMatch[1].trim()
      } else if (signerIdMatch && signerIdMatch[1]) {
        this.taskForm.patient.idCard = signerIdMatch[1].trim()
      }
      // 提取联系电话
      const phoneMatch = content.match(/联系电话[::]?\s*([^\n,,。;;]+)/)
      if (phoneMatch && phoneMatch[1]) {
        this.taskForm.patient.phone = phoneMatch[1].trim()
      }
      // 提取诊断信息
      const diagnosisMatch = content.match(/诊断[::]?\s*([^\n,,。;;]+)/)
      if (diagnosisMatch && diagnosisMatch[1]) {
        this.taskForm.patient.condition = diagnosisMatch[1].trim()
      }
      // 提取需支付转运费用(成交价)
      const priceMatch = content.match(/需支付转运费用[::]?\s*([^\n,,。;;]+)/)
      if (priceMatch && priceMatch[1]) {
        // 提取数字金额
        const priceNumber = priceMatch[1].match(/\d+(?:\.\d{1,2})?/)
        if (priceNumber) {
          this.taskForm.price = parseFloat(priceNumber[0]).toFixed(2)
        }
      }
      // 提取日期
      const dateMatch = content.match(/日期[::]?\s*([^\n,,。;;]+)/)
      if (dateMatch && dateMatch[1]) {
        const dateString = dateMatch[1].trim()
        // 尝试解析日期格式
        const dateFormatted = this.formatDateString(dateString)
        if (dateFormatted) {
          this.taskForm.transferTime = dateFormatted
        }
      }
      // 提取行程(转出医院和转入医院)
      const routeMatch = content.match(/行程[::]?\s*([^\n]+)/)
      if (routeMatch && routeMatch[1]) {
        const route = routeMatch[1].trim()
        // 按"-"分割行程,获取转出和转入医院
        const hospitals = route.split(/[-—]/).map(h => h.trim())
        if (hospitals.length >= 2) {
          // 第一个是转出医院
          this.taskForm.hospitalOut.name = hospitals[0]
          // 第二个是转入医院
          this.taskForm.hospitalIn.name = hospitals[1]
        }
      }
      // 提取家属签名或本人作为联系人
      const familyContactMatch = content.match(/(?:家属签名|本人)[::]?\s*([^\n,,。;;]+)/)
      if (familyContactMatch && familyContactMatch[1]) {
        this.taskForm.patient.contact = familyContactMatch[1].trim()
      }
      // 提取患者签名(手印)作为联系人
      const patientSignatureMatch = content.match(/患者签名(手印)[::]?\s*([^\n,,。;;]+)/)
      if (patientSignatureMatch && patientSignatureMatch[1]) {
        if (!this.taskForm.patient.contact) {
          this.taskForm.patient.contact = patientSignatureMatch[1].trim()
        }
      }
      console.log('OCR结果处理完成,表单数据更新')
    },
    // 格式化日期字符串(返回 yyyy-MM-dd HH:mm:ss 格式)
    formatDateString(dateStr) {
      // 尝试不同的日期格式
      let cleaned = dateStr.replace(/[年月]/g, '-').replace(/[日号]/g, '')
      // 支持格式:
      // - yyyy年MM月dd日HH时mm分
      // - yyyy年MM月dd日
      // - YYYYMMDD
      // - yyyy-MM-dd HH:mm:ss
      console.log('尝试格式化日期字符串:', dateStr)
      let cleaned = dateStr
        .replace(/[年月]/g, '-')
        .replace(/[日号]/g, ' ')  // 日/号 → 空格,保留日期和时间的分隔
        .replace(/时/g, ':')
        .replace(/分/g, ':')
        .replace(/秒/g, '')
        .replace(/\s+/g, ' ')  // 多个空格合并为一个
        .trim()
      console.log('清理后的日期字符串:', cleaned)
      let dateResult = ''
      
      // 如果是YYMMDD格式
@@ -2151,7 +2022,7 @@
        const year = '20' + cleaned.substring(0, 2)
        const month = cleaned.substring(2, 4)
        const day = cleaned.substring(4, 6)
        dateResult = `${year}-${month}-${day}`
        dateResult = `${year}-${month}-${day}`;
      }
      // 如果是YYYYMMDD格式
      else if (/^\d{8}$/.test(cleaned)) {
@@ -2168,6 +2039,15 @@
      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}$/)) {
        return cleaned.replace(/[//]/g, '-')
      }
      // 如果包含时分但缺少秒(yyyy-MM-dd HH:mm:)
      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}:$/)) {
        // 去掉末尾的冒号,补上秒数00
        return cleaned.replace(/[//]/g, '-').replace(/:$/, '') + ':00'
      }
      // 如果只包含时分(yyyy-MM-dd HH:mm)
      else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}$/)) {
        return cleaned.replace(/[//]/g, '-') + ':00'
      }
      else {
        dateResult = dateStr
      }