| | |
| | | <view class="info"> |
| | | <text class="case-no">{{ item.caseNo || item.reportId }}</text> |
| | | <text class="patient" |
| | | >{{ item.patName }} | {{ getGenderText(item.sex) }} | |
| | | >{{ item.patName }} | {{ item.sex == 1 ? "男" : "女" }} | |
| | | {{ item.age }}岁</text |
| | | > |
| | | </view> |
| | |
| | | }}</text> |
| | | </view> |
| | | <view class="actions"> |
| | | <!-- 待转运(1):可以开始转运 --> |
| | | <button |
| | | class="action-btn" |
| | | v-if="item.transitStatus === 1" |
| | | @tap.stop="startTransport(item)" |
| | | class="action-btn primary" |
| | | v-if="item.transitStatus == 1" |
| | | @tap.stop="handleStartTransport(item)" |
| | | > |
| | | 开始转运 |
| | | </button> |
| | | |
| | | <!-- 转运中(2):可以完成或取消 --> |
| | | <button |
| | | class="action-btn" |
| | | v-if="item.transitStatus === 2" |
| | | @tap.stop="completeTransport(item)" |
| | | class="action-btn success" |
| | | v-if="item.transitStatus == 2" |
| | | @tap.stop="handleCompleteTransport(item)" |
| | | > |
| | | 完成转运 |
| | | </button> |
| | | <button |
| | | class="action-btn secondary" |
| | | v-if="item.transitStatus != 3" |
| | | @tap.stop="editTransport(item)" |
| | | class="action-btn danger" |
| | | v-if="item.transitStatus == 2" |
| | | @tap.stop="handleCancelTransport(item)" |
| | | > |
| | | 编辑 |
| | | 取消转运 |
| | | </button> |
| | | |
| | | <!-- 已取消(4):可以恢复 --> |
| | | <button |
| | | class="action-btn warning" |
| | | v-if="item.transitStatus == 4" |
| | | @tap.stop="handleRecoverTransport(item)" |
| | | > |
| | | 恢复转运 |
| | | </button> |
| | | |
| | | <!-- 通用按钮 --> |
| | | <button class="action-btn secondary" @tap.stop="viewDetails(item)"> |
| | | 查看详情 |
| | | </button> |
| | |
| | | <!-- 空状态 --> |
| | | <view |
| | | class="empty-state" |
| | | v-if="!loading && filteredTransports.length === 0" |
| | | v-if="!loading && filteredTransports.length == 0" |
| | | > |
| | | <image src="/static/empty/no-transport.png" mode="aspectFit" /> |
| | | <text>暂无转运单记录</text> |
| | |
| | | </scroll-view> |
| | | |
| | | <!-- 操作确认弹窗 --> |
| | | <view class="modal-mask" v-if="showActionModal"> |
| | | <view class="modal-content"> |
| | | <view class="modal-mask" v-if="showActionModal" @tap="cancelAction"> |
| | | <view class="modal-content" @tap.stop> |
| | | <view class="modal-header"> |
| | | <text class="modal-title">{{ modalTitle }}</text> |
| | | </view> |
| | | <view class="modal-body"> |
| | | <text |
| | | >确定要{{ modalAction }}转运单 "{{ currentTransport.caseNo }}" |
| | | 吗?</text |
| | | <text class="modal-desc"> |
| | | 确定要{{ modalAction }}转运单 "{{ currentTransport.caseNo }}" 吗? |
| | | </text> |
| | | <view class="modal-summary" v-if="currentTransport.patName"> |
| | | <text class="summary-item" |
| | | >患者:{{ currentTransport.patName }}</text |
| | | > |
| | | <text class="summary-item" |
| | | >当前状态:{{ |
| | | getStatusText(currentTransport.transitStatus) |
| | | }}</text |
| | | > |
| | | </view> |
| | | </view> |
| | | <view class="modal-footer"> |
| | | <button class="modal-btn cancel" @tap="cancelAction">取消</button> |
| | | <button class="modal-btn confirm" @tap="confirmAction">确认</button> |
| | | <button class="modal-btn confirm" @tap="confirmAction"> |
| | | 确认{{ modalAction }} |
| | | </button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 加载状态 --> |
| | | <u-loading-page v-if="initLoading" :loading="true" text="数据加载中..." /> |
| | | |
| | | <!-- 浮动按钮 --> |
| | | <!-- <view class="float-button" @tap="createTransport"> |
| | | <u-icon name="plus" color="#fff" size="24"></u-icon> |
| | | </view> --> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | const currentTransport = ref({}); |
| | | const modalTitle = ref(""); |
| | | const modalAction = ref(""); |
| | | const modalTargetStatus = ref(null); |
| | | |
| | | // 筛选相关 |
| | | const transportTypes = [ |
| | |
| | | // 其他字段 |
| | | transportStartPlace: apiData.transportStartPlace, |
| | | remark: apiData.remark, |
| | | annexfilesList: apiData.annexfilesList || [], |
| | | }; |
| | | }; |
| | | |
| | |
| | | |
| | | // 前端状态筛选 |
| | | if (currentType.value !== "") { |
| | | result = result.filter((item) => item.transitStatus === currentType.value); |
| | | result = result.filter((item) => item.transitStatus == currentType.value); |
| | | } |
| | | |
| | | return result; |
| | |
| | | if (!gender) return "待转运"; |
| | | if (!dict.value.sys_user_sex) return gender; |
| | | const genderItem = dict.value.sys_user_sex.find( |
| | | (item) => item.dictValue === gender, |
| | | (item) => item.dictValue == gender, |
| | | ); |
| | | return genderItem ? genderItem.dictLabel : gender; |
| | | }; |
| | |
| | | } |
| | | }; |
| | | |
| | | // ============== 转运状态变更操作 ============== |
| | | |
| | | /** 开始转运 */ |
| | | const handleStartTransport = (item) => { |
| | | currentTransport.value = { ...item }; |
| | | modalTitle.value = "开始转运"; |
| | | modalAction.value = "开始"; |
| | | modalTargetStatus.value = 2; // 设置为转运中 |
| | | showActionModal.value = true; |
| | | }; |
| | | |
| | | /** 完成转运 */ |
| | | const handleCompleteTransport = (item) => { |
| | | currentTransport.value = { ...item }; |
| | | modalTitle.value = "完成转运"; |
| | | modalAction.value = "完成"; |
| | | modalTargetStatus.value = 3; // 设置为转运完成 |
| | | showActionModal.value = true; |
| | | }; |
| | | |
| | | /** 取消转运 */ |
| | | const handleCancelTransport = (item) => { |
| | | currentTransport.value = { ...item }; |
| | | modalTitle.value = "取消转运"; |
| | | modalAction.value = "取消"; |
| | | modalTargetStatus.value = 4; // 设置为转运取消 |
| | | showActionModal.value = true; |
| | | }; |
| | | |
| | | /** 恢复转运 */ |
| | | const handleRecoverTransport = (item) => { |
| | | currentTransport.value = { ...item }; |
| | | modalTitle.value = "恢复转运"; |
| | | modalAction.value = "恢复"; |
| | | modalTargetStatus.value = 2; // 设置为转运中 |
| | | showActionModal.value = true; |
| | | }; |
| | | |
| | | // 取消操作 |
| | | const cancelAction = () => { |
| | | showActionModal.value = false; |
| | | }; |
| | | |
| | | // 确认操作 |
| | | const confirmAction = async () => { |
| | | try { |
| | | const requestData = { |
| | | ...currentTransport.value, |
| | | transitStatus: modalTargetStatus.value, |
| | | }; |
| | | |
| | | // 清空附件ID(参照PC端逻辑) |
| | | if (requestData.annexfilesList && requestData.annexfilesList.length > 0) { |
| | | requestData.annexfilesList.forEach((item) => { |
| | | item.id = null; |
| | | }); |
| | | } |
| | | |
| | | const res = await uni.$uapi.post("/project/transport/edit", requestData); |
| | | |
| | | if (res && res.code == 200) { |
| | | uni.showToast({ |
| | | title: `${modalAction.value}转运成功`, |
| | | icon: "success", |
| | | }); |
| | | |
| | | // 如果是完成转运(状态变为3),同步更新案例状态 |
| | | if (modalTargetStatus.value == 3) { |
| | | try { |
| | | // 获取案例详情 |
| | | const caseRes = await uni.$uapi.get( |
| | | `/project/donatebaseinforeport/getInfo/${requestData.reportId}`, |
| | | ); |
| | | |
| | | if (caseRes && caseRes.data) { |
| | | const caseData = caseRes.data; |
| | | caseData.isDonate = 1; // 标记为已捐献 |
| | | |
| | | const editRes = await uni.$uapi.post( |
| | | "/project/donatebaseinforeport/edit", |
| | | caseData, |
| | | ); |
| | | |
| | | if (editRes && editRes.code == 200) { |
| | | uni.showToast({ |
| | | title: "对应上报案例已进入正式案例流程", |
| | | icon: "success", |
| | | }); |
| | | } |
| | | } |
| | | } catch (caseError) { |
| | | console.error("更新案例状态失败:", caseError); |
| | | // 不阻断主流程,只提示 |
| | | uni.showToast({ |
| | | title: "案例状态更新失败,请联系管理员", |
| | | icon: "none", |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // 更新本地数据 |
| | | const index = transports.value.findIndex( |
| | | (item) => item.id == currentTransport.value.id, |
| | | ); |
| | | if (index !== -1) { |
| | | transports.value[index].transitStatus = modalTargetStatus.value; |
| | | } |
| | | |
| | | // 刷新统计数据 |
| | | await loadStats(); |
| | | |
| | | // 关闭弹窗 |
| | | showActionModal.value = false; |
| | | } else { |
| | | uni.showToast({ |
| | | title: res?.msg || `${modalAction.value}转运失败`, |
| | | icon: "none", |
| | | }); |
| | | } |
| | | } catch (error) { |
| | | console.error(`${modalAction.value}转运失败:`, error); |
| | | uni.showToast({ |
| | | title: "操作失败,请稍后重试", |
| | | icon: "none", |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 查看详情 |
| | | const viewDetail = (item) => { |
| | | viewDetails(item); |
| | |
| | | // 创建转运单 |
| | | const createTransport = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/transport/create' |
| | | url: "/pages/transport/create", |
| | | }); |
| | | }; |
| | | |
| | | // 开始转运 |
| | | const startTransport = (item) => { |
| | | currentTransport.value = item; |
| | | modalTitle.value = "开始转运"; |
| | | modalAction.value = "开始"; |
| | | showActionModal.value = true; |
| | | }; |
| | | |
| | | // 完成转运 |
| | | const completeTransport = (item) => { |
| | | currentTransport.value = item; |
| | | modalTitle.value = "完成转运"; |
| | | modalAction.value = "完成"; |
| | | showActionModal.value = true; |
| | | }; |
| | | |
| | | // 确认操作 |
| | | const confirmAction = async () => { |
| | | try { |
| | | if (modalAction.value === "开始") { |
| | | await updateTransportStatus(2, "开始转运"); |
| | | } else if (modalAction.value === "完成") { |
| | | await updateTransportStatus(3, "完成转运"); |
| | | } |
| | | |
| | | uni.showToast({ |
| | | title: `${modalAction.value}成功`, |
| | | icon: "success", |
| | | }); |
| | | } catch (error) { |
| | | console.error(`${modalAction.value}失败:`, error); |
| | | uni.showToast({ |
| | | title: `${modalAction.value}失败,请重试`, |
| | | icon: "none", |
| | | }); |
| | | } finally { |
| | | showActionModal.value = false; |
| | | } |
| | | }; |
| | | |
| | | // 取消操作 |
| | | const cancelAction = () => { |
| | | showActionModal.value = false; |
| | | }; |
| | | |
| | | // 数据加载函数 |
| | |
| | | |
| | | console.log("接口返回数据:", res); |
| | | |
| | | if (res && res.code === 200) { |
| | | if (res && res.code == 200) { |
| | | // 处理不同的数据结构 |
| | | let data = []; |
| | | |
| | |
| | | |
| | | const mappedData = data.map((item) => mapApiDataToTransportItem(item)); |
| | | |
| | | if (reset || pageNum.value === 1) { |
| | | if (reset || pageNum.value == 1) { |
| | | transports.value = mappedData; |
| | | } else { |
| | | transports.value = [...transports.value, ...mappedData]; |
| | |
| | | // 加载统计数据 |
| | | const loadStats = async () => { |
| | | try { |
| | | const res = await uni.$uapi.post("/api/totalServiceTransportState"); |
| | | if (res.data) { |
| | | const res = await uni.$uapi.post( |
| | | "/project/transport/totalServiceTransportState", |
| | | ); |
| | | console.log(222); |
| | | console.log(res,'11'); |
| | | |
| | | if (res.data && Array.isArray(res.data)) { |
| | | // 使用 find 查找各状态的数量,避免硬编码索引 |
| | | const totalTransports = |
| | | res.data.reduce((sum, item) => sum + item.count, 0) || 0; |
| | | const pendingTransports = |
| | | res.data.find((item) => item.transit_status == 1)?.count || 0; |
| | | const completedTransports = |
| | | res.data.find((item) => item.transit_status == 3)?.count || 0; |
| | | |
| | | stats.value = { |
| | | totalTransports: |
| | | res.data.reduce((sum, item) => sum + item.count, 0) || 0, |
| | | pendingTransports: res.data[0].count || 0, |
| | | completedTransports: res.data[3].count || 0, |
| | | totalTransports, |
| | | pendingTransports, |
| | | completedTransports, |
| | | }; |
| | | } else { |
| | | // 数据格式异常时设置默认值 |
| | | stats.value = { |
| | | totalTransports: 0, |
| | | pendingTransports: 0, |
| | | completedTransports: 0, |
| | | }; |
| | | console.error("统计数据格式异常:", res.data); |
| | | } |
| | | } catch (error) { |
| | | console.error("加载统计失败:", error); |
| | | } |
| | | // 出错时也设置默认值,避免页面显示异常 |
| | | stats.value = { |
| | | totalTransports: 0, |
| | | pendingTransports: 0, |
| | | completedTransports: 0, |
| | | }; |
| | | |
| | | // 更新转运状态 |
| | | const updateTransportStatus = async (newStatus, actionName) => { |
| | | try { |
| | | const updateData = { |
| | | id: currentTransport.value.id, |
| | | transitStatus: newStatus, |
| | | }; |
| | | |
| | | const res = await uni.$uapi.post("/project/transport/edit", updateData); |
| | | |
| | | if (res && res.code === 200) { |
| | | // 更新本地数据 |
| | | const index = transports.value.findIndex( |
| | | (item) => item.id === currentTransport.value.id, |
| | | ); |
| | | if (index !== -1) { |
| | | transports.value[index].transitStatus = newStatus; |
| | | } |
| | | |
| | | // 更新统计数据 |
| | | await loadStats(); |
| | | |
| | | // 存储状态更新 |
| | | uni.setStorageSync("transportStatusUpdate", { |
| | | orderId: currentTransport.value.id, |
| | | status: newStatus, |
| | | }); |
| | | |
| | | return true; |
| | | } else { |
| | | throw new Error(res?.msg || `${actionName}失败`); |
| | | } |
| | | } catch (error) { |
| | | console.error(`${actionName}失败:`, error); |
| | | throw error; |
| | | } |
| | | }; |
| | | |
| | | // 处理状态更新 |
| | | const handleStatusUpdate = (update) => { |
| | | const index = transports.value.findIndex( |
| | | (item) => item.id === update.orderId, |
| | | (item) => item.id == update.orderId, |
| | | ); |
| | | if (index !== -1) { |
| | | transports.value[index].transitStatus = update.status; |
| | |
| | | } |
| | | |
| | | .transport-list { |
| | | height: calc(100vh - 400rpx); |
| | | // padding: 0 20rpx; |
| | | height: calc(100vh - 420rpx); |
| | | |
| | | .transport-item { |
| | | margin: 20rpx; |
| | |
| | | |
| | | .actions { |
| | | display: flex; |
| | | gap: 16rpx; |
| | | flex-wrap: wrap; |
| | | gap: 12rpx; |
| | | justify-content: flex-end; |
| | | |
| | | .action-btn { |
| | | height: 60rpx; |
| | | line-height: 60rpx; |
| | | padding: 0 24rpx; |
| | | font-size: 24rpx; |
| | | color: #0071e3; |
| | | background: rgba(0, 113, 227, 0.1); |
| | | border-radius: 30rpx; |
| | | height: 58rpx; |
| | | line-height: 58rpx; |
| | | padding: 0 20rpx; |
| | | font-size: 22rpx; |
| | | border-radius: 29rpx; |
| | | font-weight: 500; |
| | | transition: all 0.3s ease; |
| | | border: none; |
| | | |
| | | &.primary { |
| | | color: #0071e3; |
| | | background: rgba(0, 113, 227, 0.1); |
| | | } |
| | | |
| | | &.success { |
| | | color: #34c759; |
| | | background: rgba(52, 199, 89, 0.1); |
| | | } |
| | | |
| | | &.danger { |
| | | color: #ff3b30; |
| | | background: rgba(255, 59, 48, 0.1); |
| | | } |
| | | |
| | | &.warning { |
| | | color: #ff9500; |
| | | background: rgba(255, 149, 0, 0.1); |
| | | } |
| | | |
| | | &.secondary { |
| | | color: #515154; |
| | |
| | | |
| | | &:active { |
| | | transform: scale(0.95); |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .load-more, .no-more { |
| | | .load-more, |
| | | .no-more { |
| | | text-align: center; |
| | | padding: 40rpx 0; |
| | | color: #86868b; |
| | |
| | | .modal-content { |
| | | background: #fff; |
| | | border-radius: 32rpx; |
| | | width: 600rpx; |
| | | width: 620rpx; |
| | | overflow: hidden; |
| | | box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2); |
| | | } |
| | |
| | | } |
| | | |
| | | .modal-title { |
| | | font-size: 32rpx; |
| | | font-weight: 600; |
| | | font-size: 34rpx; |
| | | font-weight: 700; |
| | | color: #1d1d1f; |
| | | } |
| | | |
| | | .modal-body { |
| | | padding: 20rpx 40rpx 40rpx; |
| | | text-align: center; |
| | | } |
| | | |
| | | .modal-desc { |
| | | font-size: 28rpx; |
| | | color: #515154; |
| | | line-height: 1.6; |
| | | display: block; |
| | | } |
| | | |
| | | .modal-summary { |
| | | margin-top: 24rpx; |
| | | background: #f8f9fc; |
| | | border-radius: 16rpx; |
| | | padding: 20rpx; |
| | | text-align: left; |
| | | |
| | | .summary-item { |
| | | display: block; |
| | | font-size: 24rpx; |
| | | color: #86868b; |
| | | line-height: 1.8; |
| | | } |
| | | } |
| | | |
| | | .modal-footer { |
| | |
| | | &:active { |
| | | background: #f0f7ff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* 浮动按钮 */ |
| | | .float-button { |
| | | position: fixed; |
| | | bottom: 100rpx; |
| | | right: 40rpx; |
| | | width: 100rpx; |
| | | height: 100rpx; |
| | | background: #0071e3; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 8rpx 20rpx rgba(0, 113, 227, 0.3); |
| | | z-index: 100; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:active { |
| | | opacity: 0.8; |
| | | transform: scale(0.95); |
| | | } |
| | | } |
| | | } |