| | |
| | | </view> |
| | | |
| | | <!-- 附件信息 --> |
| | | <view class="detail-section"> |
| | | <view class="section-title"> |
| | | 任务附件 |
| | | <button class="upload-btn" @click="showUploadDialog">上传附件</button> |
| | | </view> |
| | | <view v-if="attachmentList && attachmentList.length > 0"> |
| | | <view class="attachment-item" v-for="(item, index) in attachmentList" :key="item.attachmentId"> |
| | | <view class="attachment-info"> |
| | | <view class="attachment-category"> |
| | | <text class="category-tag">{{ getCategoryName(item.attachmentCategory) }}</text> |
| | | </view> |
| | | <view class="attachment-name">{{ item.fileName }}</view> |
| | | <view class="attachment-meta"> |
| | | <text class="upload-time">{{ formatTime(item.uploadTime) }}</text> |
| | | <text class="file-size">{{ formatFileSize(item.fileSize) }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="attachment-actions"> |
| | | <button class="action-btn view-btn" @click="viewAttachment(item)">查看</button> |
| | | <button class="action-btn delete-btn" @click="deleteAttachment(item.attachmentId, index)">删除</button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="no-attachment"> |
| | | <text>暂无附件</text> |
| | | </view> |
| | | </view> |
| | | <AttachmentUpload |
| | | :taskId="taskId" |
| | | title="任务附件" |
| | | :readonly="isTaskFinished" |
| | | @uploaded="onAttachmentUploaded" |
| | | @deleted="onAttachmentDeleted" |
| | | /> |
| | | |
| | | <!-- 福祉车任务特有信息 --> |
| | | <view class="detail-section" v-if="taskDetail.taskType === 'WELFARE' && taskDetail.welfareInfo"> |
| | |
| | | |
| | | <!-- 已完成/已取消: 不显示按钮 --> |
| | | </view> |
| | | |
| | | <!-- 附件上传对话框 --> |
| | | <uni-popup ref="uploadPopup" type="bottom"> |
| | | <view class="upload-dialog"> |
| | | <view class="dialog-header"> |
| | | <text class="dialog-title">上传附件</text> |
| | | <uni-icons type="closeempty" size="24" @click="closeUploadDialog"></uni-icons> |
| | | </view> |
| | | <view class="dialog-content"> |
| | | <view class="form-item"> |
| | | <view class="form-label">附件分类</view> |
| | | <picker @change="onCategoryChange" :value="selectedCategoryIndex" :range="categoryList" range-key="label"> |
| | | <view class="picker-value"> |
| | | {{ categoryList[selectedCategoryIndex].label }} |
| | | <uni-icons type="arrowdown" size="16"></uni-icons> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | <view class="form-item"> |
| | | <view class="form-label">选择图片</view> |
| | | <button class="choose-image-btn" @click="chooseImage"> |
| | | <uni-icons type="image" size="20"></uni-icons> |
| | | <text>点击选择</text> |
| | | </button> |
| | | </view> |
| | | <view class="preview-area" v-if="tempImagePath"> |
| | | <image :src="tempImagePath" mode="aspectFit" class="preview-image"></image> |
| | | </view> |
| | | </view> |
| | | <view class="dialog-footer"> |
| | | <button class="cancel-btn" @click="closeUploadDialog">取消</button> |
| | | <button class="confirm-btn" @click="confirmUpload" :disabled="!tempImagePath">确定上传</button> |
| | | </view> |
| | | </view> |
| | | </uni-popup> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getTask, changeTaskStatus } from '@/api/task' |
| | | import { getAttachmentList, uploadAttachmentFromWechat, deleteAttachment, getWechatAccessToken } from '@/api/task' |
| | | import { checkVehicleActiveTasks } from '@/api/task' |
| | | import { formatDateTime } from '@/utils/common' |
| | | import AttachmentUpload from '@/components/AttachmentUpload.vue' |
| | | |
| | | export default { |
| | | components: { |
| | | AttachmentUpload |
| | | }, |
| | | data() { |
| | | return { |
| | | taskDetail: null, |
| | | taskId: null, |
| | | attachmentList: [], |
| | | categoryList: [ |
| | | { label: '知情同意书', value: '1' }, |
| | | { label: '病人资料', value: '2' }, |
| | | { label: '操作记录', value: '3' }, |
| | | { label: '出车前', value: '4' }, |
| | | { label: '出车后', value: '5' }, |
| | | { label: '系安全带', value: '6' } |
| | | ], |
| | | selectedCategoryIndex: 0, |
| | | tempImagePath: null, |
| | | isWechatMiniProgram: false // 是否是微信小程序环境 |
| | | taskId: null |
| | | } |
| | | }, |
| | | computed: { |
| | | // 判断任务是否已结束(已完成或已取消) |
| | | isTaskFinished() { |
| | | if (!this.taskDetail || !this.taskDetail.taskStatus) { |
| | | return false |
| | | } |
| | | return ['COMPLETED', 'CANCELLED'].includes(this.taskDetail.taskStatus) |
| | | }, |
| | | // 显示任务类型 |
| | | displayTaskType() { |
| | | if (!this.taskDetail || !this.taskDetail.taskType) { |
| | |
| | | onLoad(options) { |
| | | this.taskId = options.id |
| | | this.loadTaskDetail() |
| | | this.loadAttachmentList() |
| | | |
| | | // 检测是否是微信小程序环境 |
| | | // #ifdef MP-WEIXIN |
| | | this.isWechatMiniProgram = true |
| | | // #endif |
| | | }, |
| | | methods: { |
| | | // 加载任务详情 |
| | |
| | | handleTaskAction(action) { |
| | | switch (action) { |
| | | case 'depart': |
| | | // 出发 -> 状态变为出发中 |
| | | this.$modal.confirm('确定要出发吗?').then(() => { |
| | | this.updateTaskStatus('DEPARTING', '任务已出发') |
| | | }).catch(() => {}); |
| | | // 出发 -> 检查车辆是否有其他正在进行中的任务 |
| | | this.checkVehicleAndDepart(); |
| | | break; |
| | | |
| | | case 'cancel': |
| | |
| | | }).catch(() => {}); |
| | | break; |
| | | } |
| | | }, |
| | | |
| | | // 检查车辆状态并出发 |
| | | checkVehicleAndDepart() { |
| | | // 获取任务车辆ID |
| | | const vehicleId = this.getVehicleId(); |
| | | if (!vehicleId) { |
| | | this.$modal.showToast('未找到任务车辆信息'); |
| | | return; |
| | | } |
| | | |
| | | // 显示加载提示 |
| | | uni.showLoading({ |
| | | title: '检查车辆状态...' |
| | | }); |
| | | |
| | | checkVehicleActiveTasks(vehicleId).then(response => { |
| | | uni.hideLoading(); |
| | | |
| | | const activeTasks = response.data || []; |
| | | |
| | | // 过滤掉当前任务本身 |
| | | const otherActiveTasks = activeTasks.filter(task => task.taskId !== this.taskId); |
| | | |
| | | if (otherActiveTasks.length > 0) { |
| | | // 车辆有其他正在进行中的任务 |
| | | const task = otherActiveTasks[0]; |
| | | const taskStatus = this.getStatusText(task.taskStatus); |
| | | const message = `该车辆已有正在转运中的任务! |
| | | |
| | | 任务单号:${task.taskCode} |
| | | 任务状态:${taskStatus} |
| | | |
| | | 请先完成当前任务后再出发新任务。`; |
| | | |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: message, |
| | | showCancel: false, |
| | | confirmText: '我知道了' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | // 车辆没有其他正在进行中的任务,可以出发 |
| | | this.$modal.confirm('确定要出发吗?').then(() => { |
| | | this.updateTaskStatus('DEPARTING', '任务已出发') |
| | | }).catch(() => {}); |
| | | |
| | | }).catch(error => { |
| | | uni.hideLoading(); |
| | | console.error('检查车辆状态失败:', error); |
| | | // 检查失败时,仍然允许出发 |
| | | this.$modal.confirm('检查车辆状态失败,是否继续出发?').then(() => { |
| | | this.updateTaskStatus('DEPARTING', '任务已出发') |
| | | }).catch(() => {}); |
| | | }); |
| | | }, |
| | | |
| | | // 获取任务车辆ID |
| | | getVehicleId() { |
| | | if (!this.taskDetail) { |
| | | return null; |
| | | } |
| | | |
| | | // 从车辆列表中获取第一个车辆的ID |
| | | if (this.taskDetail.vehicleList && this.taskDetail.vehicleList.length > 0) { |
| | | return this.taskDetail.vehicleList[0].vehicleId; |
| | | } |
| | | |
| | | // 或者从单个车辆对象获取 |
| | | if (this.taskDetail.vehicleId) { |
| | | return this.taskDetail.vehicleId; |
| | | } |
| | | |
| | | return null; |
| | | }, |
| | | |
| | | // 更新任务状态 |
| | |
| | | if (size < 1024) return size + 'B' |
| | | if (size < 1024 * 1024) return (size / 1024).toFixed(2) + 'KB' |
| | | return (size / 1024 / 1024).toFixed(2) + 'MB' |
| | | }, |
| | | |
| | | // 附件上传成功回调 |
| | | onAttachmentUploaded(response) { |
| | | console.log('附件上传成功:', response) |
| | | }, |
| | | |
| | | // 附件删除成功回调 |
| | | onAttachmentDeleted(attachmentId) { |
| | | console.log('附件删除成功:', attachmentId) |
| | | } |
| | | } |
| | | } |
| | |
| | | padding: 20rpx; |
| | | border-radius: 10rpx; |
| | | } |
| | | |
| | | .no-attachment { |
| | | text-align: center; |
| | | padding: 40rpx 0; |
| | | color: #999; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .attachment-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | margin-bottom: 15rpx; |
| | | background-color: #f9f9f9; |
| | | border-radius: 10rpx; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .attachment-info { |
| | | flex: 1; |
| | | margin-right: 20rpx; |
| | | |
| | | .attachment-category { |
| | | margin-bottom: 8rpx; |
| | | |
| | | .category-tag { |
| | | display: inline-block; |
| | | padding: 4rpx 12rpx; |
| | | background-color: #007AFF; |
| | | color: white; |
| | | font-size: 22rpx; |
| | | border-radius: 4rpx; |
| | | } |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | margin-bottom: 8rpx; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .attachment-meta { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | |
| | | .upload-time { |
| | | margin-right: 20rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .attachment-actions { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10rpx; |
| | | |
| | | .action-btn { |
| | | padding: 8rpx 20rpx; |
| | | font-size: 24rpx; |
| | | border-radius: 6rpx; |
| | | border: none; |
| | | |
| | | &.view-btn { |
| | | background-color: #007AFF; |
| | | color: white; |
| | | } |
| | | |
| | | &.delete-btn { |
| | | background-color: #ff3b30; |
| | | color: white; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .loading { |
| | |
| | | |
| | | &:last-child { |
| | | margin-right: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .upload-dialog { |
| | | background-color: white; |
| | | border-radius: 20rpx 20rpx 0 0; |
| | | padding: 30rpx; |
| | | |
| | | .dialog-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 30rpx; |
| | | |
| | | .dialog-title { |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | } |
| | | |
| | | .dialog-content { |
| | | .form-item { |
| | | margin-bottom: 30rpx; |
| | | |
| | | .form-label { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | margin-bottom: 15rpx; |
| | | } |
| | | |
| | | .picker-value { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 20rpx; |
| | | background-color: #f5f5f5; |
| | | border-radius: 10rpx; |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | } |
| | | |
| | | .choose-image-btn { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 30rpx; |
| | | background-color: #f5f5f5; |
| | | border-radius: 10rpx; |
| | | border: 2rpx dashed #ccc; |
| | | color: #666; |
| | | font-size: 28rpx; |
| | | |
| | | text { |
| | | margin-left: 10rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .preview-area { |
| | | margin-top: 20rpx; |
| | | |
| | | .preview-image { |
| | | width: 100%; |
| | | height: 400rpx; |
| | | border-radius: 10rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | margin-top: 30rpx; |
| | | |
| | | button { |
| | | flex: 1; |
| | | height: 80rpx; |
| | | border-radius: 10rpx; |
| | | font-size: 30rpx; |
| | | border: none; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | background-color: #f5f5f5; |
| | | color: #666; |
| | | } |
| | | |
| | | .confirm-btn { |
| | | background-color: #007AFF; |
| | | color: white; |
| | | |
| | | &:disabled { |
| | | background-color: #ccc; |
| | | } |
| | | } |
| | | } |
| | | } |