| | |
| | | icon="el-icon-plus" |
| | | @click="handleAdd" |
| | | >新建工单</el-button> |
| | | <el-button |
| | | v-if="activeTab === 'pending'" |
| | | type="success" |
| | | icon="el-icon-check" |
| | | @click="openReviewDialog" |
| | | >批量审核</el-button> |
| | | <el-button type="success" plain icon="el-icon-download" @click="exportData">导出</el-button> |
| | | </template> |
| | | <template #menu="{ row }"> |
| | |
| | | <el-button type="primary" @click="submitDispatch">确认派发</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 添加在其他 dialog 组件之后 --> |
| | | <el-dialog v-model="reviewDialogVisible" title="批量审核" width="70%" append-to-body custom-class="review-dialog"> |
| | | <div class="review-container"> |
| | | <!-- 图片浏览区域 --> |
| | | <div class="review-image-wrapper"> |
| | | <!-- 左箭头 --> |
| | | <div v-show="selections.length > 1" class="arrow-button left" @click="handlePrevImage"> |
| | | <el-icon><ArrowLeft /></el-icon> |
| | | </div> |
| | | |
| | | <!-- 图片显示区域 --> |
| | | <div class="review-image-container"> |
| | | <el-image |
| | | :src="currentReviewImage" |
| | | fit="cover" |
| | | :preview-src-list="getImageList()" |
| | | :initial-index="currentImageIndex - 1" |
| | | class="preview-image" |
| | | > |
| | | <template #error> |
| | | <div class="image-error"> |
| | | <i class="el-icon-picture-outline"></i> |
| | | <span>图片加载失败</span> |
| | | </div> |
| | | </template> |
| | | </el-image> |
| | | </div> |
| | | |
| | | <!-- 右箭头 --> |
| | | <div v-show="selections.length > 1" class="arrow-button right" @click="handleNextImage"> |
| | | <el-icon><ArrowRight /></el-icon> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 分页指示器 --> |
| | | <div class="review-pagination" v-if="selections.length > 1"> |
| | | <el-pagination |
| | | small |
| | | layout="prev, pager, next" |
| | | hide-on-single-page |
| | | :total="selections.length" |
| | | :current-page.sync="currentImageIndex" |
| | | :page-size="1" |
| | | @current-change="handleImagePageChange" |
| | | > |
| | | </el-pagination> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="handleBatchApprove">通过</el-button> |
| | | <el-button type="danger" @click="handleBatchReject">不通过</el-button> |
| | | <el-button @click="reviewDialogVisible = false">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </basic-container> |
| | | </template> |
| | | |
| | |
| | | userNameToIdMap: {}, // 新增用户名到ID的映射 |
| | | workType: 0, // 新增:当前工单work_type |
| | | selections: [], // 添加选中行数据数组 |
| | | reviewDialogVisible: false, // 新增:审核对话框可见性 |
| | | currentReviewImage: '', // 新增:当前审核图片 |
| | | currentImageIndex: 1, // 新增:当前图片索引 |
| | | }; |
| | | }, |
| | | created() { |
| | |
| | | this.$message.error('地图加载失败,请检查网络或API Key配置'); |
| | | } |
| | | }, |
| | | async handleBatchApprove() { |
| | | try { |
| | | await this.$confirm('确认批量审核通过选中的工单?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }); |
| | | |
| | | const promises = this.selections.map(async (item) => { |
| | | const data = { |
| | | id: item.id, |
| | | status: item.status, |
| | | isPass: 0, // 0 表示通过 |
| | | eventNum: item.orderNumber, |
| | | eventName: item.orderName, |
| | | eventType: item.type, |
| | | }; |
| | | return flowEvent(data); |
| | | }); |
| | | |
| | | const results = await Promise.all(promises); |
| | | const successCount = results.filter(res => res.data.code === 0).length; |
| | | |
| | | if (successCount > 0) { |
| | | this.$message.success(`成功审核通过 ${successCount} 个工单`); |
| | | // 清空选择 |
| | | this.clearSelection(); |
| | | // 刷新数据 |
| | | this.fetchTableData(); |
| | | } else { |
| | | throw new Error('批量审核失败'); |
| | | } |
| | | } catch (error) { |
| | | if (error === 'cancel') return; |
| | | this.$message.error(error.message || '批量审核失败,请稍后重试'); |
| | | } |
| | | }, |
| | | async fetchDropdownData() { |
| | | try { |
| | | const response = await getTicketInfo(); |
| | |
| | | clearSelection() { |
| | | this.$refs.avueCrud.clearSelection(); |
| | | }, |
| | | |
| | | // 打开审核对话框 |
| | | openReviewDialog() { |
| | | if (this.selections.length === 0) { |
| | | this.$message.warning('请先选择要审核的工单'); |
| | | return; |
| | | } |
| | | this.currentImageIndex = 1; |
| | | this.updateCurrentReviewImage(); |
| | | this.reviewDialogVisible = true; |
| | | }, |
| | | |
| | | // 更新当前审核图片 |
| | | updateCurrentReviewImage() { |
| | | if (this.selections.length > 0 && this.currentImageIndex > 0) { |
| | | const currentItem = this.selections[this.currentImageIndex - 1]; |
| | | this.currentReviewImage = currentItem.photo_url || ''; |
| | | } else { |
| | | this.currentReviewImage = ''; |
| | | } |
| | | }, |
| | | |
| | | // 处理图片分页变化 |
| | | handleImagePageChange(page) { |
| | | this.currentImageIndex = page; |
| | | this.updateCurrentReviewImage(); |
| | | }, |
| | | |
| | | // 批量审核通过 |
| | | async handleBatchApprove() { |
| | | try { |
| | | await this.$confirm('确认批量审核通过选中的工单?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }); |
| | | |
| | | const promises = this.selections.map(async (item) => { |
| | | const data = { |
| | | id: item.id, |
| | | status: item.status, |
| | | isPass: 0, // 0 表示通过 |
| | | eventNum: item.orderNumber, |
| | | eventName: item.orderName, |
| | | eventType: item.type, |
| | | }; |
| | | return flowEvent(data); |
| | | }); |
| | | |
| | | const results = await Promise.all(promises); |
| | | const successCount = results.filter(res => res.data.code === 0).length; |
| | | |
| | | if (successCount > 0) { |
| | | this.$message.success(`成功审核通过 ${successCount} 个工单`); |
| | | // 清空选择 |
| | | this.clearSelection(); |
| | | // 刷新数据 |
| | | this.fetchTableData(); |
| | | // 关闭审核对话框 |
| | | this.reviewDialogVisible = false; |
| | | } else { |
| | | throw new Error('批量审核失败'); |
| | | } |
| | | } catch (error) { |
| | | if (error === 'cancel') return; |
| | | this.$message.error(error.message || '批量审核失败,请稍后重试'); |
| | | } |
| | | }, |
| | | |
| | | // 批量审核不通过 |
| | | async handleBatchReject() { |
| | | try { |
| | | await this.$confirm('确认批量审核不通过选中的工单?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }); |
| | | |
| | | const promises = this.selections.map(async (item) => { |
| | | const data = { |
| | | id: item.id, |
| | | status: item.status, |
| | | isPass: 1, // 1 表示不通过 |
| | | eventNum: item.orderNumber, |
| | | eventName: item.orderName, |
| | | eventType: item.type, |
| | | }; |
| | | return flowEvent(data); |
| | | }); |
| | | |
| | | const results = await Promise.all(promises); |
| | | const successCount = results.filter(res => res.data.code === 0).length; |
| | | |
| | | if (successCount > 0) { |
| | | this.$message.success(`成功审核不通过 ${successCount} 个工单`); |
| | | // 清空选择 |
| | | this.clearSelection(); |
| | | // 刷新数据 |
| | | this.fetchTableData(); |
| | | // 关闭审核对话框 |
| | | this.reviewDialogVisible = false; |
| | | } else { |
| | | throw new Error('批量审核失败'); |
| | | } |
| | | } catch (error) { |
| | | if (error === 'cancel') return; |
| | | this.$message.error(error.message || '批量审核失败,请稍后重试'); |
| | | } |
| | | }, |
| | | |
| | | // 获取所有图片列表用于预览 |
| | | getImageList() { |
| | | return this.selections |
| | | .map(item => item.photo_url) |
| | | .filter(url => url); // 过滤掉空值 |
| | | }, |
| | | |
| | | // 处理图片点击 |
| | | handleImageClick() { |
| | | // 图片点击事件由 el-image 的预览功能处理 |
| | | }, |
| | | |
| | | // 处理上一张图片 |
| | | handlePrevImage() { |
| | | if (this.currentImageIndex > 1) { |
| | | this.currentImageIndex--; |
| | | this.updateCurrentReviewImage(); |
| | | } |
| | | }, |
| | | |
| | | // 处理下一张图片 |
| | | handleNextImage() { |
| | | if (this.currentImageIndex < this.selections.length) { |
| | | this.currentImageIndex++; |
| | | this.updateCurrentReviewImage(); |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | // 添加新的样式 |
| | | .review-dialog { |
| | | :deep(.el-dialog__body) { |
| | | padding: 0; |
| | | background-color: #f5f7fa; |
| | | } |
| | | } |
| | | |
| | | .review-container { |
| | | position: relative; |
| | | |
| | | .review-image-wrapper { |
| | | position: relative; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 600px; |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .review-image-container { |
| | | width: 100%; |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | :deep(.el-image) { |
| | | width: 100%; |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | .el-image__inner { |
| | | max-height: 100%; |
| | | max-width: 100%; |
| | | object-fit: contain; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .arrow-button { |
| | | position: absolute; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 44px; |
| | | height: 44px; |
| | | background: rgba(0, 0, 0, 0.3); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | z-index: 100; |
| | | |
| | | &:hover { |
| | | background: rgba(0, 0, 0, 0.6); |
| | | } |
| | | |
| | | i { |
| | | color: #fff; |
| | | font-size: 24px; |
| | | } |
| | | |
| | | &.left { |
| | | left: 20px; |
| | | } |
| | | |
| | | &.right { |
| | | right: 20px; |
| | | } |
| | | } |
| | | |
| | | .preview-image { |
| | | max-width: 100%; |
| | | max-height: 100%; |
| | | } |
| | | |
| | | .review-pagination { |
| | | padding: 16px 0; |
| | | display: flex; |
| | | justify-content: center; |
| | | background-color: #fff; |
| | | |
| | | :deep(.el-pagination) { |
| | | .btn-prev, |
| | | .btn-next { |
| | | display: none; |
| | | } |
| | | |
| | | .el-pager { |
| | | .number { |
| | | margin: 0 4px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .image-error { |
| | | height: 100%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #909399; |
| | | |
| | | i { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | span { |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | ``` |