<template>
|
<scroll-view class="create-emergency-task-container" scroll-y="true">
|
<view class="form-header">
|
<view class="back-btn" @click="goBack">
|
<uni-icons type="arrowleft" size="20"></uni-icons>
|
</view>
|
<view class="title">创建转运任务</view>
|
<view class="smart-parse-btn" @click="showSmartParsePopup">
|
<uni-icons type="compose" size="20" color="#007AFF"></uni-icons>
|
<text>智能识别</text>
|
</view>
|
<view class="multi-photo-ocr-btn" @click="showMultiPhotoOCRPopup">
|
<uni-icons type="images" size="20" color="#FF6B00"></uni-icons>
|
<text>拍照识别</text>
|
</view>
|
</view>
|
|
<!-- 多图片拍照识别弹窗 -->
|
<uni-popup ref="multiPhotoOCRPopup" type="bottom" :safe-area="true">
|
<view class="photo-ocr-popup">
|
<view class="popup-header">
|
<view class="popup-title">
|
<uni-icons type="images" size="22" color="#FF6B00"></uni-icons>
|
<text>拍照识别 - 知情同意书</text>
|
</view>
|
<view class="popup-close" @click="closeMultiPhotoOCRPopup">
|
<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="#FF6B00"></uni-icons>
|
<text>请分别上传知情同意书的第一页和第二页,系统将自动识别并填充到表单中</text>
|
</view>
|
|
<!-- 知情同意书第一页 -->
|
<view class="page-upload-section first-page">
|
<view class="page-header">
|
<view class="page-title">
|
<view class="page-badge">
|
<uni-icons type="compose" size="18" color="#52c41a"></uni-icons>
|
<text class="page-number">1</text>
|
</view>
|
<view class="page-info">
|
<text class="page-main-title">知情同意书第一页</text>
|
<text class="page-sub-title">患者基本信息页</text>
|
</view>
|
</view>
|
<!-- 只有未上传时显示+号 -->
|
<view class="upload-btn green" @click="selectPage1Images" v-if="!page1Image">
|
<uni-icons type="plusempty" size="30" color="#52c41a"></uni-icons>
|
</view>
|
</view>
|
|
<!-- 识别字段提示 -->
|
<view class="field-hint" v-if="!page1Image">
|
<text>识别字段:患者姓名、性别、年龄、身份证号、诊断、转运费用、行程、开始时间、结束时间、家属签名</text>
|
</view>
|
|
<!-- 单图预览 -->
|
<view class="single-image-container" v-if="page1Image">
|
<image :src="page1Image" mode="aspectFit" class="preview-image"></image>
|
<view class="delete-btn" @click="deletePage1Image">
|
<uni-icons type="closeempty" size="20" color="#fff"></uni-icons>
|
</view>
|
<view class="image-status">
|
<uni-icons type="checkmarkempty" size="16" color="#fff"></uni-icons>
|
<text>已上传</text>
|
</view>
|
</view>
|
|
<!-- 第一页识别结果 -->
|
<view class="recognition-result" v-if="Object.keys(page1Fields).length > 0">
|
<view class="result-title">
|
<uni-icons type="checkmarkempty" size="18" color="#52c41a"></uni-icons>
|
<text>识别结果</text>
|
<text class="result-count">(共{{ Object.keys(page1Fields).length }}个字段)</text>
|
</view>
|
<view class="field-list">
|
<view class="field-item" v-for="(value, key) in page1Fields" :key="key">
|
<text class="field-name">{{ key }}</text>
|
<text class="field-value">{{ value || '未识别' }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 知情同意书第二页 -->
|
<view class="page-upload-section second-page">
|
<view class="page-header">
|
<view class="page-title">
|
<view class="page-badge">
|
<uni-icons type="compose" size="18" color="#FF6B00"></uni-icons>
|
<text class="page-number">2</text>
|
</view>
|
<view class="page-info">
|
<text class="page-main-title">知情同意书第二页</text>
|
<text class="page-sub-title">签名与联系信息页</text>
|
</view>
|
</view>
|
<!-- 只有未上传时显示+号 -->
|
<view class="upload-btn orange" @click="selectPage2Images" v-if="!page2Image">
|
<uni-icons type="plusempty" size="30" color="#FF6B00"></uni-icons>
|
</view>
|
</view>
|
|
<!-- 识别字段提示 -->
|
<view class="field-hint" v-if="!page2Image">
|
<text>识别字段:患者签名、签字人身份证号、签字日期、联系电话、签字人关系</text>
|
</view>
|
|
<!-- 单图预览 -->
|
<view class="single-image-container" v-if="page2Image">
|
<image :src="page2Image" mode="aspectFit" class="preview-image"></image>
|
<view class="delete-btn" @click="deletePage2Image">
|
<uni-icons type="closeempty" size="20" color="#fff"></uni-icons>
|
</view>
|
<view class="image-status">
|
<uni-icons type="checkmarkempty" size="16" color="#fff"></uni-icons>
|
<text>已上传</text>
|
</view>
|
</view>
|
|
<!-- 第二页识别结果 -->
|
<view class="recognition-result" v-if="Object.keys(page2Fields).length > 0">
|
<view class="result-title">
|
<uni-icons type="checkmarkempty" size="18" color="#FF6B00"></uni-icons>
|
<text>识别结果</text>
|
<text class="result-count">(共{{ Object.keys(page2Fields).length }}个字段)</text>
|
</view>
|
<view class="field-list">
|
<view class="field-item" v-for="(value, key) in page2Fields" :key="key">
|
<text class="field-name">{{ key }}</text>
|
<text class="field-value">{{ value || '未识别' }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<view class="popup-footer">
|
<button class="cancel-btn" @click="closeMultiPhotoOCRPopup">取消</button>
|
<button
|
class="confirm-btn"
|
@click="applyOcrResult"
|
:disabled="Object.keys(page1Fields).length === 0 && Object.keys(page2Fields).length === 0"
|
>
|
应用到表单
|
</button>
|
</view>
|
</view>
|
</uni-popup>
|
|
<view class="form-section">
|
<view class="form-item">
|
<view class="form-label required">任务车辆</view>
|
<picker mode="selector" :range="vehicles" :value="selectedVehicleIndex" @change="onVehicleChange">
|
<view class="form-input picker-input">
|
{{ selectedVehicle || '请选择任务车辆' }}
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
</view>
|
</picker>
|
</view>
|
<view class="form-item">
|
<OrganizationSelector
|
ref="organizationSelector"
|
v-model="selectedOrganizationId"
|
:required="true"
|
:auto-select-user-dept="true"
|
@change="onOrganizationChange"
|
/>
|
</view>
|
|
<DepartureSelector
|
:address.sync="departureAddress"
|
:longitude.sync="departureLongitude"
|
:latitude.sync="departureLatitude"
|
:region="selectedRegion"
|
:required="false"
|
@address-selected="onDepartureAddressSelected"
|
@location-success="onDepartureLocationSuccess"
|
/>
|
|
<view class="form-item">
|
<view class="form-label required">任务类型</view>
|
<picker mode="selector" :range="emergencyTaskTypeOptions" range-key="text" @change="onEmergencyTaskTypeChange">
|
<view class="form-input picker-input">
|
{{ selectedEmergencyTaskType || '请选择任务类型' }}
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
</view>
|
</picker>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label required">单据类型</view>
|
<picker mode="selector" :range="documentTypeOptions" range-key="text" @change="onDocumentTypeChange">
|
<view class="form-input picker-input">
|
{{ selectedDocumentType || '请选择单据类型' }}
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
</view>
|
</picker>
|
</view>
|
<StaffSelector
|
v-model="selectedStaff"
|
label="执行任务人员"
|
:required="false"
|
:auto-add-current-user="true"
|
:current-user-removable="true"
|
:branch-dept-ids="allOrganizationIds"
|
@change="onStaffChange"
|
/>
|
|
|
|
<view class="form-item">
|
<view class="form-label required">转运时间</view>
|
<uni-datetime-picker
|
v-model="taskForm.transferTime"
|
type="datetime"
|
:placeholder="'请选择转运时间'"
|
/>
|
</view>
|
|
<view class="form-section-title">患者信息</view>
|
<view class="form-item">
|
<view class="form-label required">联系人</view>
|
<input
|
class="form-input"
|
placeholder="请输入联系人"
|
v-model="taskForm.patient.contact"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label required">联系电话</view>
|
<input
|
class="form-input"
|
type="number"
|
placeholder="请输入联系电话"
|
v-model="taskForm.patient.phone"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label required">患者姓名</view>
|
<input
|
class="form-input"
|
placeholder="请输入患者姓名"
|
v-model="taskForm.patient.name"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">性别</view>
|
<view class="radio-group">
|
<label class="radio-item">
|
<radio value="male" :checked="taskForm.patient.gender === 'male'" @click="taskForm.patient.gender = 'male'" />
|
<text>男</text>
|
</label>
|
<label class="radio-item">
|
<radio value="female" :checked="taskForm.patient.gender === 'female'" @click="taskForm.patient.gender = 'female'" />
|
<text>女</text>
|
</label>
|
</view>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">患者身份证</view>
|
<input
|
class="form-input"
|
type="idcard"
|
placeholder="请输入患者身份证号"
|
v-model="taskForm.patient.idCard"
|
/>
|
</view>
|
|
<DiseaseSelector
|
v-model="selectedDiseases"
|
:other-description.sync="taskForm.patient.condition"
|
/>
|
|
<view class="form-section-title">转出医院信息</view>
|
<HospitalSelector
|
label="医院名称"
|
address-label="转出地址"
|
:required="true"
|
:show-department="false"
|
v-model="taskForm.hospitalOut"
|
:dept-id="selectedOrganizationId"
|
:region="selectedRegion"
|
@change="onHospitalOutChange"
|
@address-selected="onHospitalOutAddressSelected"
|
/>
|
<DepartmentSelector
|
label="转出科室"
|
:required="true"
|
v-model="taskForm.hospitalOut.department"
|
:department-id="taskForm.hospitalOut.departmentId"
|
:is-home="taskForm.hospitalOut.name === '家中'"
|
@change="onHospitalOutDepartmentChange"
|
/>
|
|
<view class="form-item">
|
<view class="form-label">床号</view>
|
<input
|
class="form-input"
|
placeholder="请输入床号"
|
v-model="taskForm.hospitalOut.bedNumber"
|
/>
|
</view>
|
|
<view class="form-section-title">转入医院信息</view>
|
<HospitalSelector
|
label="医院名称"
|
address-label="转入地址"
|
:required="true"
|
:show-department="false"
|
v-model="taskForm.hospitalIn"
|
:dept-id="selectedOrganizationId"
|
:region="selectedRegion"
|
@change="onHospitalInChange"
|
@address-selected="onHospitalInAddressSelected"
|
/>
|
<DepartmentSelector
|
label="转入科室"
|
:required="true"
|
v-model="taskForm.hospitalIn.department"
|
:department-id="taskForm.hospitalIn.departmentId"
|
:is-home="taskForm.hospitalIn.name === '家中'"
|
@change="onHospitalInDepartmentChange"
|
/>
|
|
<view class="form-item">
|
<view class="form-label">床号</view>
|
<input
|
class="form-input"
|
placeholder="请输入床号"
|
v-model="taskForm.hospitalIn.bedNumber"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label required">转运公里数</view>
|
<input
|
class="form-input"
|
type="digit"
|
placeholder="请输入转运公里数"
|
v-model="taskForm.transferDistance"
|
@blur="onDistanceChange"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label required">成交价</view>
|
<input
|
class="form-input"
|
type="digit"
|
placeholder="请输入成交价"
|
v-model="taskForm.price"
|
/>
|
</view>
|
|
<view class="form-actions">
|
<button class="submit-btn" @click="submitTask" :disabled="loading">
|
{{ loading ? '保存中...' : '保存' }}
|
</button>
|
</view>
|
</view>
|
|
|
<!-- 智能识别弹窗 -->
|
<uni-popup ref="smartParsePopup" type="bottom" :safe-area="true">
|
<view class="smart-parse-popup">
|
<view class="popup-header">
|
<view class="popup-title">智能识别</view>
|
<view class="popup-close" @click="closeSmartParsePopup">
|
<uni-icons type="closeempty" size="24" color="#333"></uni-icons>
|
</view>
|
</view>
|
|
<view class="parse-content">
|
<view class="parse-tip">
|
<uni-icons type="info" size="18" color="#007AFF"></uni-icons>
|
<text>粘贴或输入文本,如:"患者张三,电话13800138000,从广州某某医院转入深圳某某中心,费用¥680"</text>
|
</view>
|
<textarea
|
class="parse-textarea"
|
placeholder="请在此粘贴或输入转运信息..."
|
v-model="rawText"
|
:maxlength="-1"
|
/>
|
</view>
|
|
<view class="popup-footer">
|
<button class="cancel-btn" @click="closeSmartParsePopup">取消</button>
|
<button class="confirm-btn" @click="parseFreeText" :disabled="parseLoading">
|
{{ parseLoading ? '识别中...' : '开始识别' }}
|
</button>
|
</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>
|
|
<script>
|
import { mapState } from 'vuex'
|
import uniDatetimePicker from '@/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue'
|
import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
|
import { addTask, checkTaskDuplicate } from "@/api/task"
|
import { listAvailableVehicles, getUserBoundVehicle } from "@/api/vehicle"
|
import { searchHospitals, searchHospitalsByDeptRegion, searchHospitalsByKeywords } from "@/api/hospital"
|
import DepartureSelector from './components/DepartureSelector.vue'
|
import { calculateTianDiTuDistance } from "@/api/map"
|
import { listBranchUsers } from "@/api/system/user"
|
import { searchIcd10 } from "@/api/icd10"
|
import { calculateTransferPrice } from "@/api/price"
|
import { checkVehicleActiveTasks } from "@/api/task"
|
import { recognizeImage, batchRecognizeImages, DEFAULT_TRANSFER_ITEM_NAMES } from "@/api/ocr"
|
import config from '@/config'
|
import { getToken } from '@/utils/auth'
|
|
import { getDicts } from "@/api/dict"
|
import { getServiceOrdAreaTypes, getServiceOrderTypes } from "@/api/dictionary"
|
import { listBranchCompany, getDept } from "@/api/system/dept"
|
import MapSelector from './components/map-selector.vue'
|
import OrganizationSelector from './components/OrganizationSelector.vue'
|
import HospitalSelector from './components/HospitalSelector.vue'
|
import DiseaseSelector from './components/DiseaseSelector.vue'
|
import StaffSelector from './components/StaffSelector.vue'
|
import DepartmentSelector from './components/DepartmentSelector.vue'
|
|
export default {
|
components: {
|
uniDatetimePicker,
|
uniPopup,
|
MapSelector,
|
OrganizationSelector,
|
HospitalSelector,
|
DiseaseSelector,
|
DepartureSelector,
|
StaffSelector,
|
DepartmentSelector
|
},
|
data() {
|
return {
|
selectedVehicle: '',
|
selectedVehicleId: null,
|
selectedOrganizationId: null, // 当前选中的归属机构ID
|
allOrganizationIds: [], // 所有可选机构ID数组
|
selectedOrganizationServiceOrderClass: '', // 归属机构的服务单编码
|
selectedRegion: '', // 从归属机构中提取的地域信息(如:广州、深圳等)
|
departureAddress: '', // 出发地地址
|
departureLongitude: null, // 出发地经度
|
departureLatitude: null, // 出发地纬度
|
selectedEmergencyTaskType: '', // 选中的任务类型文本
|
selectedEmergencyTaskTypeId: null, // 选中的任务类型ID
|
selectedDocumentType: '', // 选中的单据类型文本
|
selectedDocumentTypeId: null, // 选中的单据类型ID
|
mapSelectorType: '',
|
// 人员选择相关
|
selectedStaff: [], // 已选择的人员列表
|
// 病情选择相关
|
selectedDiseases: [], // 已选择的病情列表
|
taskForm: {
|
transferTime: '',
|
patient: {
|
contact: '',
|
phone: '',
|
name: '',
|
gender: 'male',
|
idCard: '',
|
condition: ''
|
},
|
hospitalOut: {
|
id: null, // 医院ID
|
name: '',
|
department: '',
|
departmentId: null, // 科室ID
|
bedNumber: '',
|
address: ''
|
},
|
hospitalIn: {
|
id: null, // 医院ID
|
name: '',
|
department: '',
|
departmentId: null, // 科室ID
|
bedNumber: '',
|
address: ''
|
},
|
transferDistance: '',
|
price: ''
|
},
|
vehicles: [],
|
vehicleOptions: [],
|
|
emergencyTaskTypes: [], // 任务类型列表(从 SQL Server 动态加载)
|
emergencyTaskTypeOptions: [], // 任务类型选项(用于picker显示)
|
documentTypes: [], // 单据类型列表
|
documentTypeOptions: [], // 单据类型选项(用于picker显示)
|
loading: false,
|
// 智能识别相关
|
rawText: '',
|
parseLoading: false,
|
// 拍照识别相关
|
ocrImage: '',
|
ocrLoading: false,
|
// 多图片拍照识别相关
|
multiOcrImages: [],
|
multiOcrLoading: false,
|
// 分页OCR识别相关
|
currentOcrPage: 1, // 当前上传的页码:1=第一页,2=第二页
|
page1Image: '', // 第一页图片(单图)
|
page2Image: '', // 第二页图片(单图)
|
page1Fields: {}, // 第一页识别结果
|
page2Fields: {}, // 第二页识别结果
|
// 附件临时存储(OCR图片)
|
pendingAttachments: [] // 待上传的附件列表 [{ filePath: '', category: '1' }]
|
}
|
},
|
computed: {
|
...mapState({
|
currentUser: state => ({
|
userId: state.user.userId,
|
name: state.user.nickName || '张三',
|
nickName: state.user.nickName || '张三',
|
position: '司机',
|
deptId: state.user.deptId || 100,
|
phonenumber: state.user.phonenumber || '',
|
branchCompanyId: state.user.branchCompanyId,
|
branchCompanyName: state.user.branchCompanyName
|
})
|
}),
|
// 计算车辆在picker中的索引,用于默认选中
|
selectedVehicleIndex() {
|
if (!this.selectedVehicle || this.vehicles.length === 0) {
|
return 0
|
}
|
const index = this.vehicles.findIndex(v => v === this.selectedVehicle)
|
return index !== -1 ? index : 0
|
}
|
},
|
onLoad(options) {
|
// 设置默认转运时间为当前时间
|
this.setDefaultTransferTime()
|
|
// 先加载车辆列表,然后加载绑定车辆信息
|
this.getAvailableVehicles().then(() => {
|
this.getUserBoundVehicleInfo()
|
})
|
// 加载任务类型数据
|
this.loadEmergencyTaskTypes()
|
// 加载单据类型数据
|
this.loadDocumentTypes()
|
// 加载所有机构ID
|
this.loadAllOrganizationIds()
|
},
|
methods: {
|
// 获取用户绑定的车辆信息
|
getUserBoundVehicleInfo() {
|
const userId = this.currentUser.userId
|
if (!userId) {
|
console.warn('用户ID不存在,无法获取绑定车辆')
|
return
|
}
|
|
getUserBoundVehicle(userId).then(response => {
|
const boundVehicle = response.data
|
|
if (boundVehicle && boundVehicle.vehicleId) {
|
const boundVehicleNo = boundVehicle.vehicleNo
|
const boundVehicleId = boundVehicle.vehicleId
|
|
console.log('用户绑定的车辆:', boundVehicleNo, 'ID:', boundVehicleId)
|
|
// 在车辆列表中查找绑定的车辆
|
const vehicleIndex = this.vehicleOptions.findIndex(v =>
|
v.id === boundVehicleId || v.name === boundVehicleNo
|
)
|
|
if (vehicleIndex !== -1) {
|
// 设置默认选中的车辆
|
this.selectedVehicle = this.vehicleOptions[vehicleIndex].name
|
this.selectedVehicleId = this.vehicleOptions[vehicleIndex].id
|
console.log('默认选中车辆:', this.selectedVehicle)
|
} else {
|
console.warn('绑定的车辆不在可用车辆列表中')
|
}
|
} else {
|
console.log('用户未绑定车辆')
|
}
|
}).catch(error => {
|
console.error('获取用户绑定车辆信息失败:', error)
|
})
|
},
|
|
getAvailableVehicles() {
|
// 根据用户有权限管理的分公司,查询所有可用车辆
|
return listAvailableVehicles(null, 'EMERGENCY').then(response => {
|
const vehicleList = response.data || []
|
this.vehicleOptions = vehicleList.map(vehicle => ({
|
id: vehicle.vehicleId,
|
name: vehicle.vehicleNo,
|
type: vehicle.vehicleType,
|
status: vehicle.status,
|
deptNames: vehicle.deptNames || [] // 车辆归属的多个分公司
|
}))
|
// 只显示车牌号,不显示分公司
|
this.vehicles = this.vehicleOptions.map(v => v.name)
|
console.log('加载可用车辆数量:', this.vehicles.length)
|
}).catch(error => {
|
console.error('加载车辆列表失败:', error)
|
this.vehicles = []
|
})
|
},
|
|
onVehicleChange(e) {
|
const index = e.detail.value
|
this.selectedVehicle = this.vehicles[index]
|
this.selectedVehicleId = this.vehicleOptions[index]?.id
|
},
|
|
onOrganizationChange(orgData) {
|
// orgData 包含:deptId, deptName, serviceOrderClass, region, departureAddress, departureLongitude, departureLatitude
|
this.selectedOrganizationId = orgData.deptId
|
this.selectedOrganizationServiceOrderClass = orgData.serviceOrderClass
|
this.selectedRegion = orgData.region
|
|
// 自动填充出发地信息(机构的地址和坐标)
|
if (orgData.departureAddress) {
|
this.departureAddress = orgData.departureAddress
|
this.departureLongitude = orgData.departureLongitude || null
|
this.departureLatitude = orgData.departureLatitude || null
|
console.log('自动填充机构出发地:', this.departureAddress, '坐标:', this.departureLongitude, this.departureLatitude)
|
}
|
|
console.log('选中归属机构:', orgData.deptName, '部门ID:', orgData.deptId, '服务单编码:', orgData.serviceOrderClass, '地域:', orgData.region)
|
},
|
replaceRegion(region){
|
return region.replace(/(分公司|总公司|总部)$/g, '').trim();
|
},
|
|
// 加载所有机构ID
|
loadAllOrganizationIds() {
|
// 通过 OrganizationSelector 组件获取所有机构
|
const orgSelector = this.$refs.organizationSelector
|
if (orgSelector) {
|
orgSelector.reload().then(organizations => {
|
this.allOrganizationIds = organizations.map(org => org.deptId)
|
console.log('所有机构ID:', this.allOrganizationIds)
|
})
|
} else {
|
// 如果组件还未挂载,稍后重试
|
setTimeout(() => {
|
this.loadAllOrganizationIds()
|
}, 100)
|
}
|
},
|
|
// 加载任务类型数据(从 SQL Server)
|
loadEmergencyTaskTypes() {
|
getServiceOrderTypes().then(response => {
|
const list = response.data || []
|
this.emergencyTaskTypes = list
|
this.emergencyTaskTypeOptions = list.map(item => ({
|
id: item.vID,
|
text: item.vtext
|
}))
|
|
// 默认选中第一个任务类型
|
if (this.emergencyTaskTypeOptions.length > 0) {
|
this.selectedEmergencyTaskType = this.emergencyTaskTypeOptions[0].text
|
this.selectedEmergencyTaskTypeId = this.emergencyTaskTypeOptions[0].id
|
console.log('默认选中任务类型:', this.selectedEmergencyTaskType)
|
}
|
}).catch(error => {
|
console.error('加载任务类型失败:', error)
|
this.emergencyTaskTypes = []
|
this.emergencyTaskTypeOptions = []
|
})
|
},
|
|
// 任务类型选择
|
onEmergencyTaskTypeChange(e) {
|
const index = e.detail.value
|
const selected = this.emergencyTaskTypeOptions[index]
|
this.selectedEmergencyTaskType = selected.text
|
this.selectedEmergencyTaskTypeId = selected.id
|
},
|
|
|
// 加载单据类型数据
|
loadDocumentTypes() {
|
getServiceOrdAreaTypes().then(response => {
|
const list = response.data || []
|
this.documentTypes = list
|
this.documentTypeOptions = list.map(item => ({
|
id: item.vID,
|
text: item.vtext
|
}))
|
|
// 默认选中第一个单据类型
|
if (this.documentTypeOptions.length > 0) {
|
this.selectedDocumentType = this.documentTypeOptions[0].text
|
this.selectedDocumentTypeId = this.documentTypeOptions[0].id
|
console.log('默认选中单据类型:', this.selectedDocumentType)
|
}
|
}).catch(error => {
|
console.error('加载单据类型失败:', error)
|
this.documentTypes = []
|
this.documentTypeOptions = []
|
})
|
},
|
|
// 单据类型选择
|
onDocumentTypeChange(e) {
|
const index = e.detail.value
|
const selected = this.documentTypeOptions[index]
|
this.selectedDocumentType = selected.text
|
this.selectedDocumentTypeId = selected.id
|
},
|
|
// 转出医院变化
|
onHospitalOutChange(hospitalData) {
|
console.log('转出医院变化:', hospitalData)
|
// 组件已经通过 v-model 更新了 taskForm.hospitalOut
|
|
// 科室的设置由 DepartmentSelector 组件自动处理(通过 isHome 属性)
|
|
// 如果转入地址已填写,自动计算距离
|
if (this.taskForm.hospitalIn.address) {
|
// 如果两个都不是"家中",使用医院距离计算
|
if (hospitalData.name !== '家中' && this.taskForm.hospitalIn.name !== '家中') {
|
this.calculateHospitalDistance()
|
} else {
|
// 有一个是"家中",使用地址计算
|
this.calculateDistanceByManualAddress()
|
}
|
}
|
},
|
|
// 转出医院地址选择事件(简化,移除GPS处理)
|
onHospitalOutAddressSelected(data) {
|
if (this.taskForm.hospitalIn.address) {
|
this.calculateDistanceByManualAddress()
|
}
|
},
|
|
// 转入医院变化
|
onHospitalInChange(hospitalData) {
|
console.log('转入医院变化:', hospitalData)
|
// 组件已经通过 v-model 更新了 taskForm.hospitalIn
|
|
// 科室的设置由 DepartmentSelector 组件自动处理(通过 isHome 属性)
|
|
// 如果转出地址已填写,自动计算距离
|
if (this.taskForm.hospitalOut.address) {
|
// 如果两个都不是"家中",使用医院距离计算
|
if (hospitalData.name !== '家中' && this.taskForm.hospitalOut.name !== '家中') {
|
this.calculateHospitalDistance()
|
} else {
|
// 有一个是"家中",使用地址计算
|
this.calculateDistanceByManualAddress()
|
}
|
}
|
},
|
|
// 转入医院地址选择事件(简化,移除GPS处理)
|
onHospitalInAddressSelected(data) {
|
if (this.taskForm.hospitalOut.address) {
|
this.calculateDistanceByManualAddress()
|
}
|
},
|
|
// 转出科室变化
|
onHospitalOutDepartmentChange(data) {
|
if (data && typeof data === 'object') {
|
this.taskForm.hospitalOut.department = data.department
|
this.taskForm.hospitalOut.departmentId = data.departmentId
|
} else {
|
this.taskForm.hospitalOut.department = data
|
this.taskForm.hospitalOut.departmentId = null
|
}
|
},
|
|
// 转入科室变化
|
onHospitalInDepartmentChange(data) {
|
if (data && typeof data === 'object') {
|
this.taskForm.hospitalIn.department = data.department
|
this.taskForm.hospitalIn.departmentId = data.departmentId
|
} else {
|
this.taskForm.hospitalIn.department = data
|
this.taskForm.hospitalIn.departmentId = null
|
}
|
},
|
|
// 人员变化事件
|
onStaffChange(staff) {
|
console.log('选中人员变化:', staff)
|
// 组件已经通过 v-model 更新了 selectedStaff
|
},
|
calculateDistanceByManualAddress() {
|
const fromAddress = this.taskForm.hospitalOut.address
|
const toAddress = this.taskForm.hospitalIn.address
|
|
if (!fromAddress || !toAddress) {
|
return
|
}
|
|
console.log('计算手动输入地址距离:', fromAddress, '->', toAddress)
|
|
// 显示加载提示
|
uni.showLoading({
|
title: '计算距离中...'
|
})
|
|
// 调用百度地图API计算距离
|
const region = this.selectedRegion || '广州'
|
calculateTianDiTuDistance(fromAddress, toAddress)
|
.then(response => {
|
uni.hideLoading()
|
|
if (response.code === 200 && response.data) {
|
const distanceInMeters = response.data.distance
|
// 转换为公里,保疙1位小数
|
const distanceInKm = (distanceInMeters / 1000).toFixed(1)
|
this.taskForm.transferDistance = distanceInKm
|
|
console.log('距离计算成功:', distanceInKm, 'km')
|
this.$modal.showToast(`距离: ${distanceInKm}公里`)
|
|
// 距离计算成功后,自动计算成交价
|
this.calculatePrice()
|
} else {
|
console.error('距离计算失败:', response.msg)
|
this.$modal.showToast('距离计算失败,请手动输入')
|
}
|
})
|
.catch(error => {
|
uni.hideLoading()
|
console.error('距离计算失败:', error)
|
this.$modal.showToast('距离计算失败,请手动输入')
|
})
|
},
|
|
// 距离输入框失焦时触发成交价计算
|
onDistanceChange() {
|
this.calculatePrice()
|
},
|
|
// 计算成交价
|
calculatePrice() {
|
const fromAddress = this.taskForm.hospitalOut.address
|
const toAddress = this.taskForm.hospitalIn.address
|
const distance = this.taskForm.transferDistance
|
|
// 如果地址或距离不完整,不进行计算
|
if (!fromAddress || !toAddress || !distance || parseFloat(distance) <= 0) {
|
console.log('地址或距离信息不完整,跳过成交价计算')
|
return
|
}
|
|
console.log('开始计算成交价:', fromAddress, '->', toAddress, '距离:', distance)
|
|
// 调用成交价计算接口
|
calculateTransferPrice({
|
fromAddress: fromAddress,
|
toAddress: toAddress,
|
distance: parseFloat(distance),
|
region: this.selectedRegion || ''
|
}).then(response => {
|
if (response.code === 200 && response.data) {
|
const price = response.data.price
|
|
// 只有当返回的价格大于0时,才自动填充成交价
|
if (price && price > 0) {
|
this.taskForm.price = price.toFixed(2)
|
console.log('成交价计算成功:', price)
|
this.$modal.showToast(`成交价: ¥${price.toFixed(2)}`)
|
} else {
|
console.log('成交价为0,不自动填充')
|
}
|
} else {
|
console.log('成交价计算失败,保持手动输入')
|
}
|
}).catch(error => {
|
console.error('成交价计算失败:', error)
|
// 计算失败时不提示用户,允许用户手动输入
|
})
|
},
|
// 手动输入地址时计算距离
|
setDefaultTransferTime() {
|
const now = new Date()
|
const year = now.getFullYear()
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
const day = String(now.getDate()).padStart(2, '0')
|
const hours = String(now.getHours()).padStart(2, '0')
|
const minutes = String(now.getMinutes()).padStart(2, '0')
|
const seconds = String(now.getSeconds()).padStart(2, '0')
|
|
// 格式化为 YYYY-MM-DD HH:mm:ss
|
this.taskForm.transferTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
},
|
|
validateForm() {
|
if (!this.selectedVehicleId) {
|
this.$modal.showToast('请选择任务车辆')
|
return false
|
}
|
|
if (!this.selectedOrganizationId) {
|
this.$modal.showToast('请选择归属机构')
|
return false
|
}
|
|
if (!this.selectedEmergencyTaskType) {
|
this.$modal.showToast('请选择任务类型')
|
return false
|
}
|
|
if (!this.selectedDocumentType) {
|
this.$modal.showToast('请选择单据类型')
|
return false
|
}
|
|
if (!this.taskForm.transferTime) {
|
this.$modal.showToast('请选择转运时间')
|
return false
|
}
|
|
if (!this.taskForm.patient.contact) {
|
this.$modal.showToast('请输入联系人')
|
return false
|
}
|
|
if (!this.taskForm.patient.name) {
|
this.$modal.showToast('请输入患者姓名')
|
return false
|
}
|
|
if (!this.taskForm.patient.phone) {
|
this.$modal.showToast('请输入患者联系电话')
|
return false
|
}
|
|
if (!this.taskForm.hospitalOut.name) {
|
this.$modal.showToast('请输入转出医院名称')
|
return false
|
}
|
|
if (!this.taskForm.hospitalOut.department) {
|
this.$modal.showToast('请选择转出医院科室')
|
return false
|
}
|
|
if (!this.taskForm.hospitalIn.name) {
|
this.$modal.showToast('请输入转入医院名称')
|
return false
|
}
|
|
if (!this.taskForm.hospitalIn.department) {
|
this.$modal.showToast('请选择转入医院科室')
|
return false
|
}
|
|
// 如果转入医院是“家中”,必须填写地址
|
if (this.taskForm.hospitalIn.name === '家中' && !this.taskForm.hospitalIn.address) {
|
this.$modal.showToast('请输入转入地址(家中)')
|
return false
|
}
|
|
// 如果转出医院是“家中”,必须填写地址
|
if (this.taskForm.hospitalOut.name === '家中' && !this.taskForm.hospitalOut.address) {
|
this.$modal.showToast('请输入转出地址(家中)')
|
return false
|
}
|
|
if (!this.taskForm.transferDistance) {
|
this.$modal.showToast('请输入转运公里数')
|
return false
|
}
|
|
if (!this.taskForm.price) {
|
this.$modal.showToast('请输入成交价')
|
return false
|
}
|
|
return true
|
},
|
|
buildSubmitData() {
|
// 合并病情信息:选中的ICD-10疾病 + 其他描述
|
let conditionText = ''
|
if (this.selectedDiseases.length > 0) {
|
// 过滤掉病情名称为空的项,并构建病情字符串
|
const diseaseNames = this.selectedDiseases
|
.filter(d => d.icdName && d.icdName.trim())
|
.map(d => `${d.icdName}(${d.icdCode})`)
|
.join('、')
|
if (diseaseNames) {
|
conditionText = diseaseNames
|
}
|
}
|
if (this.taskForm.patient.condition && this.taskForm.patient.condition.trim()) {
|
if (conditionText) {
|
conditionText += '\n其他:' + this.taskForm.patient.condition
|
} else {
|
conditionText = this.taskForm.patient.condition
|
}
|
}
|
|
// 调试日志:检查转入医院地址
|
console.log('构建提交数据 - 转入医院:', this.taskForm.hospitalIn.name)
|
console.log('构建提交数据 - 转入医院地址:', this.taskForm.hospitalIn.address)
|
|
const submitData = {
|
taskType: 'EMERGENCY_TRANSFER',
|
deptId: this.selectedOrganizationId, // 归属机构ID(部门ID)
|
vehicleIds: this.selectedVehicleId ? [this.selectedVehicleId] : [],
|
assigneeIds: this.selectedStaff.map(staff => staff.userId), // 执行人员ID列表
|
assigneeId: this.selectedStaff.length > 0 ? this.selectedStaff[0].userId : null, // 主要执行人
|
// 执行人员详细信息(包含角色类型)
|
assignees: this.selectedStaff.map(staff => ({
|
userId: staff.userId,
|
userName: staff.nickName,
|
userType: staff.type // driver/doctor/nurse
|
})),
|
transferTime: this.taskForm.transferTime, // 转运时间
|
plannedStartTime: this.taskForm.transferTime, // 计划开始时间
|
documentTypeId: this.selectedDocumentTypeId, // 单据类型ID
|
taskTypeId: this.selectedEmergencyTaskTypeId, // 任务类型ID
|
// 病情ID列表(用于同步调度单的OrdICD_ID参数)
|
diseaseIds: this.selectedDiseases.filter(d => d.id !== null).map(d => d.id),
|
// 将转出医院地址作为出发地,转入医院地址作为目的地
|
departureAddress: this.departureAddress || this.taskForm.hospitalOut.address || '',
|
destinationAddress: this.taskForm.hospitalIn.address || '',
|
patient: {
|
...this.taskForm.patient,
|
condition: conditionText, // 使用合并后的病情信息
|
diseases: this.selectedDiseases
|
.filter(d => d.icdName && d.icdName.trim())
|
.map(d => ({
|
icdId: d.id,
|
icdCode: d.icdCode,
|
icdName: d.icdName
|
}))
|
},
|
// 医院信息(包含医院ID、科室名称、科室ID、GPS坐标等完整信息)
|
hospitalOut: {
|
...this.taskForm.hospitalOut
|
// GPS坐标由后端自动获取
|
},
|
hospitalIn: {
|
...this.taskForm.hospitalIn
|
// GPS坐标由后端自动获取
|
},
|
|
transferDistance: this.taskForm.transferDistance ? parseFloat(this.taskForm.transferDistance) : null,
|
price: this.taskForm.price ? parseFloat(this.taskForm.price) : null
|
}
|
|
// 出发地GPS坐标(优先使用自定义的出发地坐标)
|
if (this.departureLongitude && this.departureLatitude) {
|
submitData.departureLongitude = this.departureLongitude
|
submitData.departureLatitude = this.departureLatitude
|
}
|
|
// 目标地GPS坐标由后端根据转入医院地址自动获取
|
|
return submitData
|
},
|
|
submitTask() {
|
if (!this.validateForm()) {
|
return
|
}
|
this.doSubmitTask();
|
|
},
|
checkTaskDp(){
|
// 获取当前日期(格式YYYY-MM-DD)
|
const today = new Date()
|
const createDate = today.getFullYear() + '-' +
|
String(today.getMonth() + 1).padStart(2, '0') + '-' +
|
String(today.getDate()).padStart(2, '0')
|
const phone = this.taskForm.patient.phone
|
|
// 先检查是否重复
|
uni.showLoading({
|
title: '检查中...'
|
})
|
|
checkTaskDuplicate(phone, createDate).then(response => {
|
uni.hideLoading()
|
|
// 如果接口返回错误,说明存在重复
|
if (response.code !== 200) {
|
this.$modal.showToast(response.msg || '该联系电话在当天已有任务,不能重复提交')
|
return
|
}
|
|
// 检查通过,继续提交
|
this.doSubmitTask()
|
}).catch(error => {
|
uni.hideLoading()
|
console.error('重复性检查失败:', error)
|
this.$modal.showToast('检查失败,请重试')
|
})
|
},
|
// 执行实际的提交操作
|
doSubmitTask() {
|
this.$modal.confirm('确定要保存任务吗?').then(() => {
|
this.loading = true
|
const submitData = this.buildSubmitData()
|
|
addTask(submitData).then(response => {
|
this.loading = false
|
|
// 获取创建的任务ID
|
const taskId = response.taskId || (response.data && response.data.taskId) || null
|
console.log('任务创建成功,taskId:', taskId)
|
|
// 如果有待上传的附件(OCR图片),先上传附件
|
if (taskId && this.pendingAttachments.length > 0) {
|
this.uploadPendingAttachments(taskId).then(() => {
|
this.$modal.showToast('任务创建成功,附件已上传')
|
this.navigateToTaskList()
|
}).catch(error => {
|
console.error('附件上传失败:', error)
|
this.$modal.showToast('任务创建成功,但附件上传失败')
|
this.navigateToTaskList()
|
})
|
} else {
|
this.$modal.showToast('任务创建成功')
|
this.navigateToTaskList()
|
}
|
}).catch(error => {
|
this.loading = false
|
console.error('任务创建失败:', error)
|
this.$modal.showToast('任务创建失败,请重试')
|
})
|
}).catch(() => {})
|
},
|
|
// 上传待上传的附件(OCR图片)
|
uploadPendingAttachments(taskId) {
|
console.log('开始上传附件,taskId:', taskId, '附件数量:', this.pendingAttachments.length)
|
|
// 使用 Promise.all 并发上传所有附件
|
const uploadPromises = this.pendingAttachments.map(attachment => {
|
return this.uploadSingleAttachment(taskId, attachment)
|
})
|
|
return Promise.all(uploadPromises)
|
},
|
|
// 上传单个附件
|
uploadSingleAttachment(taskId, attachment) {
|
return new Promise((resolve, reject) => {
|
uni.uploadFile({
|
url: config.baseUrl + '/task/attachment/upload/' + taskId,
|
filePath: attachment.filePath,
|
name: 'file',
|
formData: {
|
'category': attachment.category
|
},
|
header: {
|
'Authorization': 'Bearer ' + getToken()
|
},
|
success: function(uploadRes) {
|
if (uploadRes.statusCode === 200) {
|
const result = JSON.parse(uploadRes.data)
|
if (result.code === 200) {
|
console.log('附件上传成功:', attachment.description)
|
resolve(result)
|
} else {
|
console.error('附件上传失败:', attachment.description, result.msg)
|
reject(result)
|
}
|
} else {
|
console.error('附件上传失败:', attachment.description, uploadRes)
|
reject(uploadRes)
|
}
|
},
|
fail: function(err) {
|
console.error('附件上传失败:', attachment.description, err)
|
reject(err)
|
}
|
})
|
})
|
},
|
|
// 跳转到任务列表
|
navigateToTaskList() {
|
setTimeout(() => {
|
// 跳转到任务列表并触发刷新
|
uni.switchTab({
|
url: '/pages/task/index',
|
success: () => {
|
// 使用事件总线通知任务列表页面刷新
|
uni.$emit('refreshTaskList')
|
}
|
})
|
}, 1000)
|
},
|
|
goBack() {
|
uni.navigateBack()
|
},
|
|
// 获取当前位置
|
getCurrentLocation() {
|
uni.showLoading({
|
title: '获取位置中...'
|
})
|
|
// 使用uni-app的GPS定位功能
|
uni.getLocation({
|
type: 'gcj02', // 返回国测局坐标,适用于国内地图 火星坐标
|
success: (res) => {
|
console.log('获取到GPS坐标:', res)
|
const latitude = res.latitude
|
const longitude = res.longitude
|
|
// 保存GPS坐标
|
this.departureLatitude = latitude
|
this.departureLongitude = longitude
|
|
// 调用逆地理编码接口,将坐标转换为地址
|
reverseGeocoder(latitude, longitude)
|
.then(response => {
|
uni.hideLoading()
|
|
if (response.code === 200 && response.data) {
|
// 获取详细地址
|
const address = response.data.address || response.data.formattedAddress || ''
|
this.departureAddress = address
|
|
console.log('逆地理编码成功:', address)
|
this.$modal.showToast('已获取当前位置')
|
} else {
|
console.error('逆地理编码失败:', response.msg)
|
this.$modal.showToast('位置解析失败,请手动输入地址')
|
}
|
})
|
.catch(error => {
|
uni.hideLoading()
|
console.error('逆地理编码失败:', error)
|
// 即使地址解析失败,也保留GPS坐标
|
this.$modal.showToast('位置解析失败,但GPS坐标已保存')
|
})
|
},
|
fail: (err) => {
|
uni.hideLoading()
|
console.error('获取位置失败:', err)
|
|
// 提示用户可能的原因
|
let errorMsg = '获取位置失败'
|
if (err.errMsg && err.errMsg.includes('auth deny')) {
|
errorMsg = '请在设置中开启位置权限'
|
} else if (err.errMsg && err.errMsg.includes('timeout')) {
|
errorMsg = '定位超时,请稍后重试'
|
}
|
|
this.$modal.showToast(errorMsg)
|
}
|
})
|
},
|
|
// 出发地地址选择(从地图建议中选择)
|
onDepartureAddressSelected(data) {
|
// data 包含: address, longitude, latitude, location
|
console.log('出发地地址选择:', data)
|
// 组件已经通过 .sync 更新了 departureAddress, departureLongitude, departureLatitude
|
},
|
|
// 出发地GPS定位成功
|
onDepartureLocationSuccess(data) {
|
// data 包含: address, longitude, latitude
|
console.log('出发地GPS定位成功:', data)
|
// 组件已经通过 .sync 更新了 departureAddress, departureLongitude, departureLatitude
|
},
|
|
// ==================== 智能识别相关方法 ====================
|
|
// 显示智能识别弹窗
|
showSmartParsePopup() {
|
this.rawText = ''
|
this.$refs.smartParsePopup.open()
|
},
|
|
// 关闭智能识别弹窗
|
closeSmartParsePopup() {
|
this.$refs.smartParsePopup.close()
|
this.rawText = ''
|
},
|
|
// 解析自由文本并填充表单
|
parseFreeText() {
|
const text = (this.rawText || '').trim()
|
if (!text) {
|
this.$modal.showToast('请先粘贴或输入文本')
|
return
|
}
|
this.parseLoading = true
|
|
const result = {
|
patientName: this.extractPatientName(text),
|
phone: this.extractPhone(text),
|
hospitalOutName: this.extractHospital(text, 'out'),
|
hospitalInName: this.extractHospital(text, 'in'),
|
departmentOut: this.extractDepartment(text, 'out'),
|
departmentIn: this.extractDepartment(text, 'in'),
|
price: this.extractPrice(text)
|
}
|
|
// 应用基础字段
|
if (result.patientName) this.taskForm.patient.name = result.patientName
|
if (result.phone) this.taskForm.patient.phone = result.phone
|
if (result.price) this.taskForm.price = result.price
|
|
// 应用科室信息(通过 DepartmentSelector 组件处理)
|
if (result.departmentOut) {
|
this.taskForm.hospitalOut.department = result.departmentOut
|
// 科室ID会在 DepartmentSelector 组件中自动匹配
|
}
|
if (result.departmentIn) {
|
this.taskForm.hospitalIn.department = result.departmentIn
|
// 科室ID会在 DepartmentSelector 组件中自动匹配
|
}
|
|
// 处理医院名称 → 精确匹配医院并补全地址与ID(不限制分公司区域)
|
Promise.all([
|
this.findHospitalByName(result.hospitalOutName, 'out', false),
|
this.findHospitalByName(result.hospitalInName, 'in', false)
|
])
|
.then(([outHosp, inHosp]) => {
|
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 || ''
|
this.hospitalOutSearchKeyword = outHosp.hospName
|
} else {
|
this.taskForm.hospitalOut.address = ''
|
this.taskForm.hospitalOut.department = '其它'
|
this.hospitalOutSearchKeyword = '家中'
|
}
|
} else if (result.hospitalOutName) {
|
this.taskForm.hospitalOut.name = result.hospitalOutName
|
this.hospitalOutSearchKeyword = result.hospitalOutName
|
}
|
|
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 || ''
|
this.hospitalInSearchKeyword = inHosp.hospName
|
} else {
|
this.taskForm.hospitalIn.address = ''
|
this.taskForm.hospitalIn.department = '其它'
|
this.hospitalInSearchKeyword = '家中'
|
}
|
} else if (result.hospitalInName) {
|
this.taskForm.hospitalIn.name = result.hospitalInName
|
this.hospitalInSearchKeyword = result.hospitalInName
|
}
|
|
// 若两端地址完整,自动计算距离与成交价
|
if (this.taskForm.hospitalOut.address && this.taskForm.hospitalIn.address &&
|
this.taskForm.hospitalOut.name !== '家中' && this.taskForm.hospitalIn.name !== '家中') {
|
this.calculateHospitalDistance()
|
}
|
})
|
.catch(err => {
|
console.error('医院匹配失败:', err)
|
})
|
.finally(() => {
|
this.parseLoading = false
|
this.closeSmartParsePopup()
|
this.$modal.showToast('识别完成,请核对自动填充结果')
|
})
|
},
|
|
// 提取手机号(优先匹配移动号,次匹配座机)
|
extractPhone(text) {
|
// 先去除文本中的空格,再进行匹配
|
const cleanText = text.replace(/\s+/g, '')
|
|
// 匹配11位手机号(支持原文本中有空格的情况)
|
const mobile = cleanText.match(/(?<!\d)(1[3-9]\d{9})(?!\d)/)
|
if (mobile) return mobile[1]
|
|
// 匹配座机号(支持带区号和连字符)
|
const landline = cleanText.match(/0\d{2,3}-?\d{7,8}/)
|
if (landline) return landline[0]
|
|
// 兜底:在原文本中查找带空格的手机号格式(如:182 8569 1756)
|
const mobileWithSpace = text.match(/(?:电话|手机|联系|tel|phone)[::\s]*([1][3-9][\d\s]{9,15})/i)
|
if (mobileWithSpace) {
|
const cleanMobile = mobileWithSpace[1].replace(/\s+/g, '')
|
if (/^1[3-9]\d{9}$/.test(cleanMobile)) {
|
return cleanMobile
|
}
|
}
|
|
// 最后尝试:直接查找任意带空格的11位数字组合
|
const anyMobile = text.match(/([1][3-9][\d\s]{9,15})/)
|
if (anyMobile) {
|
const cleanAny = anyMobile[1].replace(/\s+/g, '')
|
if (/^1[3-9]\d{9}$/.test(cleanAny)) {
|
return cleanAny
|
}
|
}
|
|
return ''
|
},
|
|
// 提取患者姓名(常见关键词前缀)
|
extractPatientName(text) {
|
const m = text.match(/(?:患者|病人|姓名|联系人)[:: ]?\s*([\u4e00-\u9fa5]{2,4})/)
|
return m ? m[1] : ''
|
},
|
|
// 提取成交价(支持¥/元/RMB等)
|
extractPrice(text) {
|
const m1 = text.match(/(?:成交价|价格|费用|收费|总价|共)[:: ]?\s*(?:¥|¥|RMB|人民币)?\s*([0-9]+(?:\.[0-9]{1,2})?)/i)
|
if (m1) return parseFloat(m1[1]).toFixed(2)
|
const m2 = text.match(/(?:¥|¥)\s*([0-9]+(?:\.[0-9]{1,2})?)/)
|
if (m2) return parseFloat(m2[1]).toFixed(2)
|
const m3 = text.match(/([0-9]+(?:\.[0-9]{1,2})?)\s*(?:元|人民币|RMB)/i)
|
if (m3) return parseFloat(m3[1]).toFixed(2)
|
return ''
|
},
|
|
// 提取医院名称(基于语义标记,支持简称如"省医")
|
extractHospital(text, type) {
|
// 先尝试语义标记提取
|
const outReg = /(?:转出|来自|从)[:: ]?\s*([^\s,,。;;转]+?(?:医院|中心|卫生院|急救中心|家中|省医|市医|区医|县医|人民医院|中医院|妇幼|儿童医院))/
|
const inReg = /(?:转入|至|前往|到|去往|转回)[:: ]?\s*([^\s,,。;;转]+?(?:医院|中心|卫生院|急救中心|家中|省医|市医|区医|县医|人民医院|中医院|妇幼|儿童医院))/
|
const reg = type === 'out' ? outReg : inReg
|
const m = text.match(reg)
|
if (m) {
|
// 去除可能包含的楼栋、楼层、科室等信息
|
let hospitalName = m[1]
|
// 去除楼栋信息(如:东一号楼、A栋等)
|
hospitalName = hospitalName.replace(/[东西南北中]?[一二三四五六七八九十0-9]+号?楼.*/g, '')
|
// 去除楼层信息(如:四楼、3F等)
|
hospitalName = hospitalName.replace(/[一二三四五六七八九十0-9]+[楼层F].*/g, '')
|
return hospitalName.trim()
|
}
|
|
// 无语义标记时,兜底提取首个"含医院/中心/家中/简称"的片段
|
const any = text.match(/([^\s,,。;;转]+?(?:医院|中心|卫生院|急救中心|家中|省医|市医|区医|县医|人民医院|中医院|妇幼|儿童医院))/g)
|
if (any && any.length > 0) {
|
if (type === 'out') {
|
let name = any[0]
|
name = name.replace(/[东西南北中]?[一二三四五六七八九十0-9]+号?楼.*/g, '')
|
name = name.replace(/[一二三四五六七八九十0-9]+[楼层F].*/g, '')
|
return name.trim()
|
}
|
if (type === 'in') {
|
let name = any[1] || any[0]
|
name = name.replace(/[东西南北中]?[一二三四五六七八九十0-9]+号?楼.*/g, '')
|
name = name.replace(/[一二三四五六七八九十0-9]+[楼层F].*/g, '')
|
return name.trim()
|
}
|
}
|
return ''
|
},
|
|
// 提取科室信息
|
extractDepartment(text, type) {
|
// 常见科室关键词(作为兜底方案)
|
const departments = [
|
'ICU', 'NICU', 'PICU', 'CCU', 'EICU',
|
'重症监护室', '急诊科', '门诊', '住院部',
|
'内科', '外科', '妇产科', '儿科', '骨科', '神经科',
|
'心内科', '心外科', '呼吸科', '消化科', '肾内科',
|
'血液科', '肿瘤科', '感染科', '皮肤科', '眼科',
|
'耳鼻喉科', '口腔科', '中医科', '康复科', '精神科',
|
'泌尿科', '内分泌科', '风湿科', '普外科', '胸外科',
|
'神经外科', '整形科', '烧伤科', '麻醉科', '放射科',
|
'检验科', '病理科', '药剂科', '营养科'
|
]
|
|
// 使用默认科室列表匹配
|
const sortedDepts = departments.sort((a, b) => b.length - a.length)
|
const deptPattern = sortedDepts.join('|')
|
|
const regex = new RegExp(`(${deptPattern})`, 'g')
|
const matches = text.match(regex)
|
|
if (matches && matches.length > 0) {
|
// 如果是转出,取第一个科室;如果是转入,取最后一个科室
|
return type === 'out' ? matches[0] : matches[matches.length - 1]
|
}
|
|
return ''
|
},
|
|
// 通过名称匹配医院(restrictRegion=false 时全局查询)
|
findHospitalByName(name, type, restrictRegion = true) {
|
if (!name) return Promise.resolve(null)
|
const normalized = name.trim()
|
|
// 特殊处理"家中"
|
if (normalized === '家中') {
|
// 查询医院库中的"家中"记录
|
const deptId = this.selectedOrganizationId || null
|
const queryPromise = restrictRegion && deptId
|
? searchHospitalsByDeptRegion('家中', deptId, 50)
|
: searchHospitals('家中', null, 50)
|
|
return queryPromise.then(res => {
|
const list = res.data || []
|
// 查找名称为"家中"的医院记录
|
const homeHospital = list.find(h => h.hospName === '家中')
|
if (homeHospital) {
|
return homeHospital
|
}
|
// 如果没有找到,返回默认结构
|
return {
|
hospId: null,
|
hospName: '家中',
|
hopsCity: '',
|
hospAddress: ''
|
}
|
}).catch(() => {
|
// 查询失败,返回默认结构
|
return {
|
hospId: null,
|
hospName: '家中',
|
hopsCity: '',
|
hospAddress: ''
|
}
|
})
|
}
|
|
// OCR识别后的医院名称,使用新的分词匹配接口
|
const deptId = this.selectedOrganizationId || null
|
|
return searchHospitalsByKeywords(normalized, deptId, 50).then(res => {
|
const rawData = res.data || []
|
if (!rawData.length) return null
|
|
// 提取 hospital 对象(接口返回格式:{hospital: {...}, matchScore: ...})
|
const firstItem = rawData[0]
|
const firstHospital = firstItem.hospital || firstItem
|
|
console.log(`OCR识别医院"${normalized}",自动选中:${firstHospital.hospName}(匹配分数:${firstItem.matchScore || 'N/A'})`)
|
return firstHospital
|
}).catch(error => {
|
console.error(`搜索医院"${normalized}"失败:`, error)
|
return null
|
})
|
},
|
|
// 在候选中选择最优匹配(支持简称匹配、包含与长度接近)
|
pickBestHospitalMatch(list, name) {
|
const n = name.trim()
|
|
// 过滤掉"家中",优先选择真实医院
|
const realHospitals = list.filter(h => h.hospName !== '家中')
|
const searchList = realHospitals.length > 0 ? realHospitals : list
|
|
// 1. 完全相等(正式名)
|
let best = searchList.find(h => (h.hospName || '').trim() === n)
|
if (best) return best
|
// 2. 完全相等(简称)
|
best = searchList.find(h => (h.hospShort || '').trim() === n)
|
if (best) return best
|
// 3. 包含(正式名)
|
best = searchList.find(h => (h.hospName || '').includes(n) || n.includes((h.hospName || '')))
|
if (best) return best
|
// 4. 包含(简称)
|
best = searchList.find(h => (h.hospShort || '').includes(n) || n.includes((h.hospShort || '')))
|
if (best) return best
|
// 5. 长度最接近
|
best = [...searchList].sort((a, b) => {
|
const da = Math.abs((a.hospName || '').length - n.length)
|
const db = Math.abs((b.hospName || '').length - n.length)
|
return da - db
|
})[0]
|
|
// 如果没有找到任何匹配,返回第一个
|
return best || searchList[0] || null
|
},
|
|
// 合并医院地址(省 + 市 + 区 + 详细地址)
|
buildFullAddress(hospital) {
|
const parts = []
|
if (hospital.hopsProvince) {
|
parts.push(hospital.hopsProvince)
|
}
|
if (hospital.hopsCity) {
|
parts.push(hospital.hopsCity)
|
}
|
if (hospital.hopsArea) {
|
parts.push(hospital.hopsArea)
|
}
|
if (hospital.hospAddress) {
|
parts.push(hospital.hospAddress)
|
}
|
return parts.join('')
|
},
|
|
// 自动计算两个医院之间的距离
|
calculateHospitalDistance() {
|
const fromAddress = this.taskForm.hospitalOut.address
|
const fromCity = this.taskForm.hospitalOut.city
|
const toAddress = this.taskForm.hospitalIn.address
|
const toCity = this.taskForm.hospitalIn.city
|
|
if (!fromAddress || !toAddress) {
|
console.log('地址信息不完整,无法计算距离')
|
return
|
}
|
|
console.log('开始计算距离:', fromAddress, '->', toAddress)
|
|
// 显示加载提示
|
uni.showLoading({
|
title: '计算距离中...'
|
})
|
|
// 调用百度地图API计算距离
|
calculateTianDiTuDistance(fromAddress, toAddress)
|
.then(response => {
|
uni.hideLoading()
|
|
if (response.code === 200 && response.data) {
|
const distanceInMeters = response.data.distance
|
// 转换为公里,保留1位小数
|
const distanceInKm = (distanceInMeters / 1000).toFixed(1)
|
this.taskForm.transferDistance = distanceInKm
|
|
console.log('距离计算成功:', distanceInKm, 'km')
|
// this.$modal.showToast(`距离计算成功: ${distanceInKm}公里`)
|
|
// 距离计算成功后,自动计算成交价
|
this.calculatePrice()
|
} else {
|
console.error('距离计算失败:', response.msg)
|
this.$modal.showToast('距离计算失败,请手动输入')
|
}
|
})
|
.catch(error => {
|
uni.hideLoading()
|
console.error('距离计算失败:', error)
|
this.$modal.showToast('距离计算失败,请手动输入')
|
})
|
},
|
|
// ==================== 拍照识别相关方法 ====================
|
|
// 显示拍照识别弹窗
|
showPhotoOCRPopup() {
|
this.ocrImage = ''
|
this.$refs.photoOCRPopup.open()
|
},
|
|
// 显示多图拍照识别弹窗
|
showMultiPhotoOCRPopup() {
|
this.page1Image = ''
|
this.page2Image = ''
|
this.page1Fields = {}
|
this.page2Fields = {}
|
this.$refs.multiPhotoOCRPopup.open()
|
},
|
|
// 关闭多图拍照识别弹窗
|
closeMultiPhotoOCRPopup() {
|
this.$refs.multiPhotoOCRPopup.close()
|
},
|
|
// 选择第一页图片
|
selectPage1Images() {
|
uni.showActionSheet({
|
itemList: ['从相册选择', '拍照'],
|
success: (res) => {
|
if (res.tapIndex === 0) {
|
this.chooseImageForPage(1, 'album')
|
} else if (res.tapIndex === 1) {
|
this.chooseImageForPage(1, 'camera')
|
}
|
}
|
})
|
},
|
|
// 选择第二页图片
|
selectPage2Images() {
|
uni.showActionSheet({
|
itemList: ['从相册选择', '拍照'],
|
success: (res) => {
|
if (res.tapIndex === 0) {
|
this.chooseImageForPage(2, 'album')
|
} else if (res.tapIndex === 1) {
|
this.chooseImageForPage(2, 'camera')
|
}
|
}
|
})
|
},
|
|
// 删除第一页图片
|
deletePage1Image() {
|
this.page1Image = ''
|
this.page1Fields = {}
|
},
|
|
// 删除第二页图片
|
deletePage2Image() {
|
this.page2Image = ''
|
this.page2Fields = {}
|
},
|
|
// 为指定页码选择图片(单图)
|
chooseImageForPage(page, sourceType) {
|
uni.chooseImage({
|
count: 1, // 只选择一张图片
|
sizeType: ['compressed'],
|
sourceType: [sourceType],
|
success: (res) => {
|
const imagePath = res.tempFilePaths[0]
|
|
if (page === 1) {
|
this.page1Image = imagePath
|
} else {
|
this.page2Image = imagePath
|
}
|
|
// 选择完图片后,立即进行OCR识别
|
this.$modal.showToast(`已选择图片,开始识别...`)
|
this.recognizeSinglePage(page)
|
},
|
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('拍照失败')
|
}
|
})
|
},
|
|
// 应用OCR识别结果到表单
|
applyOcrResult() {
|
// 合并两页的识别结果
|
const mergedFields = { ...this.page1Fields, ...this.page2Fields }
|
|
// 处理合并后的识别结果
|
this.processMultiOCRResult(mergedFields)
|
|
// 保存OCR图片到待上传附件列表(知情同意书,分类为'1')
|
this.pendingAttachments = []
|
if (this.page1Image) {
|
this.pendingAttachments.push({
|
filePath: this.page1Image,
|
category: '1', // 知情同意书
|
description: '知情同意书第一页'
|
})
|
}
|
if (this.page2Image) {
|
this.pendingAttachments.push({
|
filePath: this.page2Image,
|
category: '1', // 知情同意书
|
description: '知情同意书第二页'
|
})
|
}
|
|
console.log('保存待上传附件:', this.pendingAttachments)
|
|
// 关闭弹窗
|
this.closeMultiPhotoOCRPopup()
|
|
// 清空图片和结果(但保留 pendingAttachments)
|
this.page1Image = ''
|
this.page2Image = ''
|
this.page1Fields = {}
|
this.page2Fields = {}
|
|
this.$modal.showToast('已应用到表单,知情同意书将在任务创建后上传')
|
},
|
|
// 识别单个页码的图片(选择后立即识别)
|
recognizeSinglePage(page) {
|
const image = page === 1 ? this.page1Image : this.page2Image
|
|
if (!image) {
|
return
|
}
|
|
// 显示加载提示
|
uni.showLoading({
|
title: `识别第${page}页...`
|
})
|
|
// 第一页的itemNames
|
const page1ItemNames = [
|
"患者姓名", "性别", "年龄", "身份证号", "诊断",
|
"需支付转运费用", "行程", "开始时间", "结束时间", "家属签名"
|
]
|
|
// 第二页的itemNames
|
const page2ItemNames = [
|
"患者签名(手印)", "签字人身份证号码", "签字人身份证号", "日期",
|
"联系电话", "本人", "签字人与患者关系"
|
]
|
|
const itemNames = page === 1 ? page1ItemNames : page2ItemNames
|
|
// 调用批量OCR API(传入单个图片)
|
batchRecognizeImages([image], itemNames)
|
.then(result => {
|
uni.hideLoading()
|
|
if (result.success && result.successCount > 0) {
|
// 保存识别结果
|
if (page === 1) {
|
this.page1Fields = result.fields
|
} else {
|
this.page2Fields = result.fields
|
}
|
|
console.log(`第${page}页识别结果:`, result.fields)
|
this.$modal.showToast(`第${page}页识别成功`)
|
} else {
|
this.$modal.showToast(`第${page}页识别失败`)
|
}
|
})
|
.catch(error => {
|
uni.hideLoading()
|
console.error(`第${page}页OCR识别失败:`, error)
|
this.$modal.showToast(error.msg || `第${page}页识别失败`)
|
})
|
},
|
|
// 处理多图OCR识别结果
|
processMultiOCRResult(fields) {
|
console.log('多图OCR识别结果:', fields)
|
|
// 提取患者姓名
|
if (fields['患者姓名']) {
|
this.taskForm.patient.name = fields['患者姓名'].trim()
|
}
|
|
// 提取联系人(优先患者签名,其次家属签名,最后本人)
|
if (fields['患者签名(手印)']) {
|
this.taskForm.patient.contact = fields['患者签名(手印)'].trim()
|
} else if (fields['家属签名']) {
|
this.taskForm.patient.contact = fields['家属签名'].trim()
|
} else if (fields['本人']) {
|
this.taskForm.patient.contact = fields['本人'].trim()
|
}
|
|
// 提取性别
|
if (fields['性别']) {
|
const gender = fields['性别'].trim()
|
if (gender.includes('男')) {
|
this.taskForm.patient.gender = 'male'
|
} else if (gender.includes('女')) {
|
this.taskForm.patient.gender = 'female'
|
}
|
}
|
|
// 提取身份证号(优先身份证号,其次签字人身份证号码)
|
const patientIdCard = fields['身份证号'] || fields['患者身份证号']
|
const signerIdCard = fields['签字人身份证号码'] || fields['签字人身份证号']
|
|
if (patientIdCard) {
|
this.taskForm.patient.idCard = patientIdCard.trim()
|
} else if (signerIdCard) {
|
this.taskForm.patient.idCard = signerIdCard.trim()
|
}
|
|
// 提取日期(转运时间)
|
if (fields['日期']) {
|
const dateString = fields['日期'].trim()
|
const dateFormatted = this.formatDateString(dateString)
|
if (dateFormatted) {
|
this.taskForm.transferTime = dateFormatted
|
}
|
}
|
|
// 提取联系电话
|
if (fields['联系电话']) {
|
this.taskForm.patient.phone = fields['联系电话'].trim()
|
}
|
|
// 提取需支付转运费用(成交价)
|
if (fields['需支付转运费用']) {
|
const priceText = fields['需支付转运费用'].trim()
|
const priceNumber = priceText.match(/\d+(?:\.\d{1,2})?/)
|
if (priceNumber) {
|
this.taskForm.price = parseFloat(priceNumber[0]).toFixed(2)
|
}
|
}
|
|
// 提取诊断(病情)
|
if (fields['诊断']) {
|
this.taskForm.patient.condition = fields['诊断'].trim()
|
}
|
|
// 提取行程(转出医院和转入医院)
|
if (fields['行程']) {
|
const route = fields['行程'].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]
|
|
// 尝试从医院库中匹配并补全地址
|
Promise.all([
|
this.findHospitalByName(hospitals[0], 'out', false),
|
this.findHospitalByName(hospitals[1], 'in', false)
|
]).then(([outHosp, inHosp]) => {
|
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 || ''
|
}
|
}
|
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 || ''
|
}
|
}
|
|
// 如果两个医院地址都有,自动计算距离
|
if (this.taskForm.hospitalOut.address && this.taskForm.hospitalIn.address &&
|
this.taskForm.hospitalOut.name !== '家中' && this.taskForm.hospitalIn.name !== '家中') {
|
this.calculateHospitalDistance()
|
}
|
})
|
}
|
}
|
|
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, '')
|
|
let dateResult = ''
|
|
// 如果是YYMMDD格式
|
if (/^\d{6}$/.test(cleaned)) {
|
const year = '20' + cleaned.substring(0, 2)
|
const month = cleaned.substring(2, 4)
|
const day = cleaned.substring(4, 6)
|
dateResult = `${year}-${month}-${day}`
|
}
|
// 如果是YYYYMMDD格式
|
else if (/^\d{8}$/.test(cleaned)) {
|
const year = cleaned.substring(0, 4)
|
const month = cleaned.substring(4, 6)
|
const day = cleaned.substring(6, 8)
|
dateResult = `${year}-${month}-${day}`
|
}
|
// 如果已经是合理格式,直接使用
|
else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}$/)) {
|
dateResult = cleaned.replace(/[//]/g, '-')
|
}
|
// 如果已经包含时分秒,直接返回
|
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, '-')
|
}
|
else {
|
dateResult = dateStr
|
}
|
|
// 如果日期格式正确,添加默认时分秒 00:00:00
|
if (dateResult && dateResult.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) {
|
return dateResult + ' 00:00:00'
|
}
|
|
return dateResult
|
}
|
}
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.create-emergency-task-container {
|
padding: 20rpx;
|
background-color: #f5f5f5;
|
min-height: 100vh;
|
|
.form-header {
|
display: flex;
|
align-items: center;
|
padding: 20rpx 0;
|
margin-bottom: 30rpx;
|
|
.back-btn {
|
width: 60rpx;
|
height: 60rpx;
|
border-radius: 50%;
|
background-color: #f0f0f0;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-right: 20rpx;
|
}
|
|
.title {
|
flex: 1;
|
font-size: 36rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
|
.smart-parse-btn,
|
.ocr-page-btn {
|
position: relative;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
padding: 10rpx 20rpx;
|
|
text {
|
font-size: 22rpx;
|
margin-top: 4rpx;
|
}
|
|
.badge {
|
position: absolute;
|
top: 0;
|
right: 10rpx;
|
min-width: 32rpx;
|
height: 32rpx;
|
line-height: 32rpx;
|
text-align: center;
|
background-color: #ff4d4f;
|
color: white;
|
font-size: 20rpx;
|
border-radius: 16rpx;
|
padding: 0 8rpx;
|
}
|
}
|
|
.smart-parse-btn {
|
text {
|
color: #007AFF;
|
}
|
}
|
|
.ocr-page-btn:first-of-type {
|
text {
|
color: #52c41a;
|
}
|
}
|
|
.ocr-page-btn:last-of-type {
|
text {
|
color: #FF6B00;
|
}
|
}
|
|
.multi-photo-ocr-btn {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
padding: 10rpx 20rpx;
|
|
text {
|
font-size: 22rpx;
|
color: #FF6B00;
|
margin-top: 4rpx;
|
}
|
}
|
}
|
|
.form-section {
|
background-color: white;
|
border-radius: 15rpx;
|
padding: 30rpx;
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
.form-section-title {
|
font-size: 32rpx;
|
font-weight: bold;
|
margin: 40rpx 0 20rpx 0;
|
padding-bottom: 10rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
}
|
|
.form-item {
|
margin-bottom: 40rpx;
|
|
.form-label {
|
font-size: 28rpx;
|
margin-bottom: 15rpx;
|
color: #333;
|
|
&.required::before {
|
content: '*';
|
color: #ff4d4f;
|
margin-right: 4rpx;
|
font-weight: bold;
|
}
|
}
|
|
.hospital-search-container {
|
position: relative;
|
|
.search-results {
|
position: absolute;
|
top: 75rpx;
|
left: 0;
|
right: 0;
|
max-height: 400rpx;
|
background-color: white;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
z-index: 100;
|
overflow-y: auto;
|
|
.search-result-item {
|
padding: 20rpx 30rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
&:active {
|
background-color: #f5f5f5;
|
}
|
|
.hospital-name {
|
font-size: 28rpx;
|
color: #333;
|
font-weight: bold;
|
margin-bottom: 8rpx;
|
|
.hospital-short {
|
display: block;
|
font-size: 22rpx;
|
color: #999;
|
font-weight: normal;
|
margin-top: 6rpx;
|
}
|
}
|
|
.hospital-address {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
}
|
}
|
|
.address-input-container {
|
position: relative;
|
|
.address-suggestions {
|
position: absolute;
|
top: 75rpx;
|
left: 0;
|
right: 0;
|
max-height: 400rpx;
|
background-color: white;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
z-index: 100;
|
overflow-y: auto;
|
|
.address-suggestion-item {
|
padding: 20rpx 30rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
&:active {
|
background-color: #f5f5f5;
|
}
|
|
.suggestion-name {
|
font-size: 28rpx;
|
color: #333;
|
font-weight: bold;
|
margin-bottom: 8rpx;
|
}
|
|
.suggestion-address {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
}
|
}
|
|
.form-input {
|
height: 70rpx;
|
padding: 0 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
|
&.picker-input {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
|
&.disabled {
|
background-color: #f5f5f5;
|
color: #999;
|
}
|
}
|
}
|
|
.form-textarea {
|
width: 100%;
|
min-height: 150rpx;
|
padding: 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
}
|
|
.form-tip {
|
margin-top: 10rpx;
|
font-size: 24rpx;
|
color: #999;
|
line-height: 1.5;
|
}
|
|
.disease-container {
|
.disease-tags {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 15rpx;
|
margin-bottom: 20rpx;
|
|
.disease-tag {
|
display: flex;
|
align-items: center;
|
padding: 10rpx 20rpx;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
border-radius: 30rpx;
|
|
.disease-name {
|
font-size: 26rpx;
|
color: white;
|
margin-right: 10rpx;
|
}
|
}
|
}
|
|
.add-disease-btn {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding: 20rpx;
|
border: 1rpx dashed #007AFF;
|
border-radius: 10rpx;
|
color: #007AFF;
|
font-size: 28rpx;
|
|
text {
|
margin-left: 10rpx;
|
}
|
}
|
}
|
|
.radio-group {
|
display: flex;
|
|
.radio-item {
|
display: flex;
|
align-items: center;
|
margin-right: 30rpx;
|
|
radio {
|
margin-right: 10rpx;
|
}
|
}
|
}
|
|
.staff-list {
|
.staff-item {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 20rpx;
|
background-color: #f9f9f9;
|
border-radius: 10rpx;
|
margin-bottom: 20rpx;
|
|
.staff-info {
|
flex: 1;
|
display: flex;
|
align-items: center;
|
|
.staff-name {
|
font-size: 28rpx;
|
color: #333;
|
margin-right: 10rpx;
|
}
|
|
.staff-role {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
}
|
|
.add-staff {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding: 20rpx;
|
border: 1rpx dashed #007AFF;
|
border-radius: 10rpx;
|
color: #007AFF;
|
}
|
}
|
}
|
|
.form-actions {
|
margin-top: 50rpx;
|
text-align: center;
|
|
.submit-btn {
|
width: 80%;
|
height: 80rpx;
|
background-color: #007AFF;
|
color: white;
|
border-radius: 10rpx;
|
font-size: 32rpx;
|
|
&[disabled] {
|
background-color: #ccc;
|
color: #999;
|
}
|
}
|
}
|
}
|
}
|
|
// 智能识别弹窗样式
|
.smart-parse-popup {
|
background-color: white;
|
border-radius: 20rpx 20rpx 0 0;
|
max-height: 80vh;
|
display: flex;
|
flex-direction: column;
|
|
.popup-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 30rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
flex-shrink: 0;
|
|
.popup-title {
|
font-size: 32rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
|
.popup-close {
|
padding: 10rpx;
|
}
|
}
|
|
.parse-content {
|
flex: 1;
|
padding: 30rpx;
|
overflow-y: auto;
|
|
.parse-tip {
|
display: flex;
|
align-items: flex-start;
|
padding: 20rpx;
|
background-color: #f0f7ff;
|
border-radius: 10rpx;
|
margin-bottom: 20rpx;
|
|
text {
|
flex: 1;
|
margin-left: 10rpx;
|
font-size: 24rpx;
|
color: #666;
|
line-height: 1.6;
|
}
|
}
|
|
.parse-textarea {
|
width: 100%;
|
min-height: 300rpx;
|
padding: 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
line-height: 1.6;
|
}
|
}
|
|
.popup-footer {
|
display: flex;
|
padding: 20rpx 30rpx;
|
border-top: 1rpx solid #f0f0f0;
|
gap: 20rpx;
|
flex-shrink: 0;
|
|
button {
|
flex: 1;
|
height: 80rpx;
|
border-radius: 10rpx;
|
font-size: 30rpx;
|
}
|
|
.cancel-btn {
|
background-color: #f5f5f5;
|
color: #666;
|
}
|
|
.confirm-btn {
|
background-color: #007AFF;
|
color: white;
|
|
&[disabled] {
|
background-color: #ccc;
|
color: #999;
|
}
|
}
|
}
|
}
|
|
// 拍照识别弹窗样式
|
.photo-ocr-popup {
|
background-color: white;
|
border-radius: 20rpx 20rpx 0 0;
|
max-height: 80vh;
|
display: flex;
|
flex-direction: column;
|
|
.popup-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 30rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
flex-shrink: 0;
|
|
.popup-title {
|
display: flex;
|
align-items: center;
|
gap: 10rpx;
|
font-size: 32rpx;
|
font-weight: bold;
|
color: #333;
|
|
text {
|
margin-left: 8rpx;
|
}
|
}
|
|
.popup-close {
|
padding: 10rpx;
|
}
|
}
|
|
.ocr-content {
|
flex: 1;
|
padding: 30rpx;
|
overflow-y: auto;
|
|
.ocr-tip {
|
display: flex;
|
align-items: flex-start;
|
padding: 20rpx;
|
background-color: #f0f7ff;
|
border-radius: 10rpx;
|
margin-bottom: 20rpx;
|
|
text {
|
flex: 1;
|
margin-left: 10rpx;
|
font-size: 24rpx;
|
color: #666;
|
line-height: 1.6;
|
}
|
}
|
|
.image-preview {
|
margin-bottom: 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
overflow: hidden;
|
}
|
|
.multi-image-preview {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 15rpx;
|
margin-bottom: 20rpx;
|
|
.image-item {
|
position: relative;
|
width: 200rpx;
|
height: 200rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
overflow: hidden;
|
|
image {
|
width: 100%;
|
height: 100%;
|
}
|
|
.delete-btn {
|
position: absolute;
|
top: 5rpx;
|
right: 5rpx;
|
width: 40rpx;
|
height: 40rpx;
|
background-color: rgba(0, 0, 0, 0.6);
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
}
|
}
|
|
.ocr-actions {
|
display: flex;
|
gap: 20rpx;
|
|
button {
|
flex: 1;
|
height: 80rpx;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
}
|
|
.select-btn {
|
background-color: #f5f5f5;
|
color: #333;
|
}
|
|
.capture-btn {
|
background-color: #007AFF;
|
color: white;
|
}
|
}
|
|
.image-count {
|
text-align: center;
|
margin-top: 20rpx;
|
font-size: 26rpx;
|
color: #FF6B00;
|
font-weight: bold;
|
}
|
|
// 页面上传区域
|
.page-upload-section {
|
background: linear-gradient(135deg, #f9f9f9 0%, #ffffff 100%);
|
border-radius: 15rpx;
|
padding: 25rpx;
|
margin-bottom: 25rpx;
|
border: 2rpx solid #f0f0f0;
|
transition: all 0.3s;
|
|
&.first-page {
|
border-left: 4rpx solid #52c41a;
|
|
.page-badge {
|
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
|
.page-number {
|
color: #52c41a;
|
}
|
}
|
}
|
|
&.second-page {
|
border-left: 4rpx solid #FF6B00;
|
|
.page-badge {
|
background: linear-gradient(135deg, #FF6B00 0%, #ff8c3a 100%);
|
|
.page-number {
|
color: #FF6B00;
|
}
|
}
|
}
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20rpx;
|
|
.page-title {
|
display: flex;
|
align-items: center;
|
gap: 15rpx;
|
flex: 1;
|
|
.page-badge {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
width: 60rpx;
|
height: 60rpx;
|
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
border-radius: 50%;
|
position: relative;
|
|
.page-number {
|
position: absolute;
|
bottom: -2rpx;
|
right: -2rpx;
|
width: 24rpx;
|
height: 24rpx;
|
background-color: #fff;
|
border-radius: 50%;
|
font-size: 16rpx;
|
font-weight: bold;
|
color: #52c41a;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
}
|
}
|
|
.page-info {
|
display: flex;
|
flex-direction: column;
|
gap: 4rpx;
|
|
.page-main-title {
|
font-size: 28rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
|
.page-sub-title {
|
font-size: 22rpx;
|
color: #999;
|
}
|
}
|
}
|
|
.upload-btn {
|
width: 60rpx;
|
height: 60rpx;
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
transition: all 0.3s;
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
&.green {
|
border: 2rpx dashed #52c41a;
|
background-color: rgba(82, 196, 26, 0.05);
|
}
|
|
&.orange {
|
border: 2rpx dashed #FF6B00;
|
background-color: rgba(255, 107, 0, 0.05);
|
}
|
|
&:active {
|
transform: scale(0.95);
|
opacity: 0.8;
|
}
|
}
|
}
|
|
// 识别字段提示
|
.field-hint {
|
background-color: #fffbe6;
|
border: 1rpx solid #ffe58f;
|
border-radius: 8rpx;
|
padding: 15rpx;
|
margin-bottom: 15rpx;
|
|
text {
|
font-size: 22rpx;
|
color: #d48806;
|
line-height: 1.6;
|
}
|
}
|
|
.images-container {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 10rpx;
|
margin-bottom: 15rpx;
|
|
.image-item {
|
position: relative;
|
width: 150rpx;
|
height: 150rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
overflow: hidden;
|
|
image {
|
width: 100%;
|
height: 100%;
|
}
|
|
.delete-btn {
|
position: absolute;
|
top: 5rpx;
|
right: 5rpx;
|
width: 35rpx;
|
height: 35rpx;
|
background-color: rgba(0, 0, 0, 0.6);
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
}
|
}
|
|
// 单图预览区域
|
.single-image-container {
|
position: relative;
|
width: 100%;
|
height: 400rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
overflow: hidden;
|
margin-bottom: 15rpx;
|
background-color: #f5f5f5;
|
|
.preview-image {
|
width: 100%;
|
height: 100%;
|
}
|
|
.delete-btn {
|
position: absolute;
|
top: 10rpx;
|
right: 10rpx;
|
width: 50rpx;
|
height: 50rpx;
|
background-color: rgba(0, 0, 0, 0.6);
|
border-radius: 50%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
z-index: 2;
|
|
&:active {
|
transform: scale(0.95);
|
opacity: 0.9;
|
}
|
}
|
|
// 图片状态标签
|
.image-status {
|
position: absolute;
|
bottom: 10rpx;
|
left: 10rpx;
|
display: flex;
|
align-items: center;
|
gap: 5rpx;
|
padding: 8rpx 15rpx;
|
background-color: rgba(82, 196, 26, 0.9);
|
border-radius: 20rpx;
|
z-index: 2;
|
|
text {
|
font-size: 22rpx;
|
color: #fff;
|
font-weight: 500;
|
}
|
}
|
}
|
|
.recognition-result {
|
background-color: white;
|
border-radius: 10rpx;
|
padding: 15rpx;
|
border: 1rpx solid #e0e0e0;
|
|
.result-title {
|
display: flex;
|
align-items: center;
|
margin-bottom: 15rpx;
|
padding-bottom: 10rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
text {
|
margin-left: 8rpx;
|
font-size: 26rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
|
.result-count {
|
margin-left: 8rpx;
|
font-size: 22rpx;
|
color: #999;
|
font-weight: normal;
|
}
|
}
|
|
.field-list {
|
.field-item {
|
display: flex;
|
padding: 10rpx 0;
|
border-bottom: 1rpx solid #f9f9f9;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
.field-name {
|
min-width: 150rpx;
|
font-size: 24rpx;
|
color: #666;
|
font-weight: 500;
|
|
&::after {
|
content: ':';
|
margin-left: 4rpx;
|
}
|
}
|
|
.field-value {
|
flex: 1;
|
font-size: 24rpx;
|
color: #333;
|
word-break: break-all;
|
}
|
}
|
}
|
}
|
}
|
|
.page-indicator {
|
display: flex;
|
justify-content: center;
|
gap: 30rpx;
|
margin-top: 30rpx;
|
|
.page-dot {
|
padding: 15rpx 30rpx;
|
border: 2rpx solid #ddd;
|
border-radius: 30rpx;
|
font-size: 26rpx;
|
color: #999;
|
background-color: #f5f5f5;
|
transition: all 0.3s;
|
|
&.active {
|
border-color: #007AFF;
|
background-color: #007AFF;
|
color: white;
|
}
|
|
&.completed {
|
border-color: #52c41a;
|
color: #52c41a;
|
|
&.active {
|
background-color: #007AFF;
|
border-color: #007AFF;
|
color: white;
|
}
|
}
|
}
|
}
|
}
|
|
.popup-footer {
|
display: flex;
|
padding: 20rpx 30rpx;
|
border-top: 1rpx solid #f0f0f0;
|
gap: 20rpx;
|
flex-shrink: 0;
|
flex-wrap: wrap;
|
|
button {
|
flex: 1;
|
min-width: 160rpx;
|
height: 80rpx;
|
border-radius: 10rpx;
|
font-size: 30rpx;
|
}
|
|
.cancel-btn {
|
background-color: #f5f5f5;
|
color: #666;
|
}
|
|
.next-btn,
|
.prev-btn {
|
background-color: #52c41a;
|
color: white;
|
}
|
|
.confirm-btn {
|
background-color: #007AFF;
|
color: white;
|
|
&[disabled] {
|
background-color: #ccc;
|
color: #999;
|
}
|
}
|
}
|
}
|
</style>
|