| | |
| | | <view class="section-title">基本信息</view> |
| | | <view class="info-item"> |
| | | <view class="label">任务编号</view> |
| | | <view class="value">{{ taskDetail.taskCode }}</view> |
| | | <view class="value"> |
| | | {{ taskDetail.showTaskCode }} |
| | | <text v-if="taskDetail.isHeadPush === '1'" class="head-push-tag">总</text> |
| | | </view> |
| | | </view> |
| | | <view class="info-item"> |
| | | <view class="label">任务类型</view> |
| | |
| | | <view |
| | | class="assignee-item" |
| | | v-for="(assignee, index) in taskDetail.assignees" |
| | | :key="'assignee-' + (assignee.userId || assignee.userName || index)" |
| | | :key="getAssigneeKey(assignee, index)" |
| | | > |
| | | <view class="assignee-index">{{ index + 1 }}</view> |
| | | <view class="assignee-info"> |
| | |
| | | }" |
| | | > |
| | | {{ getUserTypeLabel(assignee.userType) }} |
| | | </view> |
| | | <view |
| | | class="ready-badge" |
| | | :class="{ |
| | | 'ready': isAssigneeReady(assignee), |
| | | 'unready': !isAssigneeReady(assignee) |
| | | }" |
| | | > |
| | | {{ isAssigneeReady(assignee) ? '已就绪' : '未就绪' }} |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | <view |
| | | class="payment-record-item" |
| | | v-for="(payment, index) in paymentInfo.paidPayments" |
| | | :key="'payment-' + (payment.id || index)" |
| | | :key="getPaymentKey(payment, index)" |
| | | > |
| | | <view class="payment-header"> |
| | | <view |
| | |
| | | > |
| | | 修改 |
| | | </button> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction('depart')" |
| | | > |
| | | 出发 |
| | | </button> |
| | | <button |
| | | class="action-btn cancel" |
| | | @click="handleTaskAction('cancel')" |
| | | > |
| | | 取消 |
| | | </button> |
| | | <template v-if="isCurrentUserAssignee()"> |
| | | <button |
| | | v-if="showAssigneeReadyFeature() && isMultipleAssignees() && !isCurrentUserReady()" |
| | | class="action-btn primary" |
| | | @click="handleReadyAction()" |
| | | > |
| | | 就绪 |
| | | </button> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleDepartAction()" |
| | | > |
| | | 出发 |
| | | </button> |
| | | <button |
| | | class="action-btn cancel" |
| | | @click="handleTaskAction('cancel')" |
| | | > |
| | | 取消 |
| | | </button> |
| | | </template> |
| | | </template> |
| | | |
| | | <!-- 出发中状态: 显示编辑、已到达、强制结束 --> |
| | |
| | | > |
| | | 修改 |
| | | </button> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction('arrive')" |
| | | > |
| | | 已到达 |
| | | </button> |
| | | <button |
| | | class="action-btn cancel" |
| | | @click="handleTaskAction('forceCancel')" |
| | | > |
| | | 强制结束 |
| | | </button> |
| | | <template v-if="isCurrentUserAssignee()"> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction('arrive')" |
| | | > |
| | | 已到达 |
| | | </button> |
| | | <button |
| | | class="action-btn cancel" |
| | | @click="handleTaskAction('forceCancel')" |
| | | > |
| | | 强制结束 |
| | | </button> |
| | | </template> |
| | | </template> |
| | | |
| | | <!-- 已到达状态: 显示编辑、已返程 --> |
| | |
| | | > |
| | | 修改 |
| | | </button> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction('return')" |
| | | > |
| | | 已返程 |
| | | </button> |
| | | <template v-if="isCurrentUserAssignee()"> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction('return')" |
| | | > |
| | | 已返程 |
| | | </button> |
| | | </template> |
| | | </template> |
| | | |
| | | <!-- 返程中状态: 显示编辑、已完成 --> |
| | |
| | | > |
| | | 修改 |
| | | </button> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction('complete')" |
| | | > |
| | | 已完成 |
| | | </button> |
| | | <template v-if="isCurrentUserAssignee()"> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction('complete')" |
| | | > |
| | | 已完成 |
| | | </button> |
| | | </template> |
| | | </template> |
| | | |
| | | <!-- 已完成/已取消: 不显示按钮,但如果是转运任务则显示结算按钮 --> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { getTask, changeTaskStatus } from '@/api/task' |
| | | import { getTask, changeTaskStatus, setAssigneeReady } from '@/api/task' |
| | | import { checkVehicleActiveTasks } from '@/api/task' |
| | | import { getPaymentInfo } from '@/api/payment' |
| | | import { formatDateTime } from '@/utils/common' |
| | | import { validateTaskForDepart, validateTaskForSettlement, getTaskVehicleId, checkTaskCanDepart } from '@/utils/taskValidator' |
| | | import AttachmentUpload from './components/AttachmentUpload.vue' |
| | | import config from '@/config' |
| | | |
| | | export default { |
| | | components: { |
| | |
| | | |
| | | // 返回上一页 |
| | | goBack() { |
| | | uni.navigateBack() |
| | | // 检查是否有页面可以返回 |
| | | uni.navigateBack({ |
| | | delta: 1, |
| | | fail: () => { |
| | | // 如果无法返回,则跳转到任务列表页面 |
| | | uni.switchTab({ |
| | | url: '/pages/task/index' |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 处理编辑按钮 |
| | |
| | | handleTaskAction(action) { |
| | | switch (action) { |
| | | case 'depart': |
| | | // 出发 -> 检查车辆是否有其他正在进行中的任务 |
| | | this.checkVehicleAndDepart(); |
| | | this.ensureReadyThenDepart(); |
| | | break; |
| | | |
| | | case 'cancel': |
| | |
| | | // 附件删除成功回调 |
| | | onAttachmentDeleted(attachmentId) { |
| | | console.log('附件删除成功:', attachmentId) |
| | | } |
| | | }, |
| | | |
| | | // 是否显示“就绪”功能(配置开关) |
| | | showAssigneeReadyFeature() { |
| | | return !!(config && config.features && config.features.showAssigneeReadyButton) |
| | | }, |
| | | |
| | | // 当前用户是否为该执行人 |
| | | isAssigneeSelf(assignee) { |
| | | const userId = this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.userId |
| | | return assignee && (assignee.userId === userId || assignee.oaUserId === userId) |
| | | }, |
| | | |
| | | // 执行人点击“就绪” |
| | | markAssigneeReady(assignee) { |
| | | if (!assignee || !this.taskDetail) { |
| | | this.$modal.showToast('执行人或任务信息不存在') |
| | | return |
| | | } |
| | | const userId = assignee.userId || assignee.oaUserId |
| | | if (!userId) { |
| | | this.$modal.showToast('无法识别执行人ID') |
| | | return |
| | | } |
| | | this.$modal.showLoading && this.$modal.showLoading('提交中...') |
| | | setAssigneeReady(this.taskId).then(() => { |
| | | this.$modal.hideLoading && this.$modal.hideLoading() |
| | | this.$modal.showToast('已就绪') |
| | | // 刷新任务详情 |
| | | this.loadTaskDetail() |
| | | }).catch(err => { |
| | | this.$modal.hideLoading && this.$modal.hideLoading() |
| | | console.error('标记就绪失败:', err) |
| | | this.$modal.showToast('标记就绪失败') |
| | | }) |
| | | }, |
| | | |
| | | // 是否当前用户是任务执行人 |
| | | isCurrentUserAssignee() { |
| | | const userId = this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.userId; |
| | | console.log("当前用户ID:", userId) |
| | | const list = (this.taskDetail && Array.isArray(this.taskDetail.assignees)) ? this.taskDetail.assignees : [] |
| | | return list.some(a => a && (a.userId === userId || a.oaUserId === userId)) |
| | | }, |
| | | |
| | | // 是否多人执行 |
| | | isMultipleAssignees() { |
| | | const list = (this.taskDetail && Array.isArray(this.taskDetail.assignees)) ? this.taskDetail.assignees : [] |
| | | return list.length > 1 |
| | | }, |
| | | |
| | | // 执行人是否已就绪 |
| | | isAssigneeReady(assignee) { |
| | | if (!assignee) return false |
| | | return assignee.isReady === '1' || assignee.ready === true || assignee.readyStatus === 'READY' || assignee.readyFlag === 'Y' |
| | | }, |
| | | |
| | | // 所有执行人是否已就绪 |
| | | areAllAssigneesReady() { |
| | | const list = (this.taskDetail && Array.isArray(this.taskDetail.assignees)) ? this.taskDetail.assignees : [] |
| | | if (list.length === 0) return false |
| | | return list.every(a => this.isAssigneeReady(a)) |
| | | }, |
| | | |
| | | // 获取当前用户对应的执行人记录 |
| | | getCurrentUserAssignee() { |
| | | const userId = this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.userId |
| | | console.log('userId', userId) |
| | | const list = (this.taskDetail && Array.isArray(this.taskDetail.assignees)) ? this.taskDetail.assignees : [] |
| | | return list.find(a => a && (a.userId === userId || a.oaUserId === userId)) || null |
| | | }, |
| | | |
| | | // 操作区就绪按钮(多人任务) |
| | | markCurrentAssigneeReady() { |
| | | const me = this.getCurrentUserAssignee() |
| | | if (!me) { |
| | | this.$modal.showToast('仅任务执行人可操作') |
| | | return |
| | | } |
| | | this.markAssigneeReady(me) |
| | | }, |
| | | |
| | | // 当前用户是否已就绪 |
| | | isCurrentUserReady() { |
| | | const me = this.getCurrentUserAssignee() |
| | | return me ? this.isAssigneeReady(me) : false |
| | | }, |
| | | |
| | | // 处理就绪按钮点击 |
| | | async handleReadyAction() { |
| | | const me = this.getCurrentUserAssignee() |
| | | if (!me) { |
| | | this.$modal.showToast('仅任务执行人可操作') |
| | | return |
| | | } |
| | | try { |
| | | await setAssigneeReady(this.taskId) |
| | | this.$modal.showToast('已就绪') |
| | | // 刷新任务详情 |
| | | await this.loadTaskDetail() |
| | | } catch (err) { |
| | | console.error('标记就绪失败:', err) |
| | | this.$modal.showToast('标记就绪失败') |
| | | } |
| | | }, |
| | | |
| | | // 处理出发按钮点击 |
| | | async handleDepartAction() { |
| | | if (!this.taskDetail) return |
| | | |
| | | const list = (this.taskDetail && Array.isArray(this.taskDetail.assignees)) ? this.taskDetail.assignees : [] |
| | | |
| | | // 如果开启了就绪功能且是多人任务,需要检查所有人是否就绪 |
| | | if (this.showAssigneeReadyFeature() && list.length > 1) { |
| | | if (!this.areAllAssigneesReady()) { |
| | | this.$modal.showToast('其他人未就绪,所有人就绪后才能出发') |
| | | return |
| | | } |
| | | } |
| | | |
| | | // 单人任务或未开启就绪功能:自动标记就绪 |
| | | if (this.showAssigneeReadyFeature() && list.length === 1) { |
| | | const me = this.getCurrentUserAssignee() |
| | | if (me && !this.isAssigneeReady(me)) { |
| | | try { |
| | | await setAssigneeReady(this.taskId) |
| | | } catch (e) { |
| | | console.error('自动就绪失败:', e) |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 执行出发流程 |
| | | this.checkVehicleAndDepart() |
| | | }, |
| | | |
| | | // 出发前保证就绪(保留向后兼容) |
| | | async ensureReadyThenDepart() { |
| | | this.handleDepartAction() |
| | | }, |
| | | |
| | | // 获取执行人员的key值 |
| | | getAssigneeKey(assignee, index) { |
| | | // 确保返回有效的字符串key |
| | | if (!assignee) return 'assignee-' + index; |
| | | // 优先使用userId,其次是userName,最后使用index |
| | | const key = assignee.userId || assignee.userName || index; |
| | | return 'assignee-' + (key !== null && key !== undefined ? key : index); |
| | | }, |
| | | |
| | | // 获取支付记录的key值 |
| | | getPaymentKey(payment, index) { |
| | | // 确保返回有效的字符串key |
| | | if (!payment) return 'payment-' + index; |
| | | // 优先使用id,其次使用index |
| | | const key = payment.id || index; |
| | | return 'payment-' + (key !== null && key !== undefined ? key : index); |
| | | }, |
| | | |
| | | } |
| | | } |
| | | </script> |
| | |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | } |
| | | |
| | | // 总部推送标记样式 |
| | | .head-push-tag { |
| | | color: #ff0000; |
| | | font-size: 24rpx; |
| | | font-weight: bold; |
| | | margin-left: 10rpx; |
| | | padding: 2rpx 8rpx; |
| | | border: 1rpx solid #ff0000; |
| | | border-radius: 4rpx; |
| | | } |
| | | |
| | | .detail-content { |
| | |
| | | background-color: #AF52DE; |
| | | } |
| | | } |
| | | |
| | | .assignee-ready-btn { |
| | | margin-left: 12rpx; |
| | | padding: 8rpx 16rpx; |
| | | font-size: 24rpx; |
| | | border-radius: 6rpx; |
| | | background-color: #34C759; |
| | | color: #fff; |
| | | border: none; |
| | | } |
| | | .ready-badge { |
| | | display: inline-block; |
| | | margin-left: 12rpx; |
| | | padding: 4rpx 12rpx; |
| | | font-size: 22rpx; |
| | | border-radius: 6rpx; |
| | | &.ready { |
| | | background-color: #e6ffed; |
| | | color: #34C759; |
| | | } |
| | | &.unready { |
| | | background-color: #f0f0f0; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |