From f4548acea8b1e364aa08bcbbb5c448bb88c73476 Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Wed, 19 Nov 2025 19:19:40 +0800
Subject: [PATCH] feat:详情demo
---
src/views/tickets/ticket.vue | 548 ++++++++++++++++++++++++++++++++++++-----------------
src/views/tickets/ticketComponent/TicketDetailDialog.vue | 36 +++
2 files changed, 402 insertions(+), 182 deletions(-)
diff --git a/src/views/tickets/ticket.vue b/src/views/tickets/ticket.vue
index 0ba858c..504a4b6 100644
--- a/src/views/tickets/ticket.vue
+++ b/src/views/tickets/ticket.vue
@@ -270,18 +270,18 @@
</el-tabs>
<!-- 新建工单对话框 -->
- <create-ticket-dialog
- v-model="dialogVisible"
- :departments="departments"
- :department-users="departmentUsers"
- :types="types"
- :all-algorithms="allAlgorithms"
- :edit-data="editFormData"
- :map-center="inputMapShowDefaultCenter"
- @create-success="handleCreateSuccess"
- @draft-success="handleDraftSuccess"
- @create-error="handleCreateError"
- />
+ <create-ticket-dialog
+ v-model="dialogVisible"
+ :departments="departments"
+ :department-users="departmentUsers"
+ :types="types"
+ :all-algorithms="allAlgorithms"
+ :edit-data="editFormData"
+ :map-center="inputMapShowDefaultCenter"
+ @create-success="handleCreateSuccess"
+ @draft-success="handleDraftSuccess"
+ @create-error="handleCreateError"
+/>
<!-- 工单详情对话框 -->
<ticket-detail-dialog
@@ -448,7 +448,13 @@
data() {
return {
currentIndex: null, // 当前显示的数据索引
-
+
+
+ approveLoading: false,
+ rejectLoading: false,
+ dispatchLoading: false,
+ completeLoading: false,
+ finalizeLoading: false,
createoredit: '',
activeTab: 'all',
// tabs 只保留静态结构,不做权限判断
@@ -552,7 +558,7 @@
},
dialogVisible: false,
editFormData: null, // 专门用于存储编辑数据的对象
-
+ createoredit: '', // 可以删除这个字段,因为组件内部会判断
detailVisible: false,
currentDetail: {},
@@ -568,6 +574,8 @@
dispatchDepartment: '', // 新增:派发部门
dispatchHandler: '', // 新增:派发处理人
dispatchDialogVisible: false, // 新增:派发对话框可见性
+
+ stepInfos: [], // 新增:存储步骤信息
fixedStatuses: ['2', '0', '3', '4'], // 固定的五个状态
userNameToIdMap: {}, // 新增用户名到ID的映射
workType: 0, // 新增:当前工单work_type
@@ -575,7 +583,7 @@
reviewDialogVisible: false, // 新增:审核对话框可见性
currentReviewImage: '', // 新增:当前审核图片
currentImageIndex: 1, // 新增:当前图片索引
-
+ totalTime: '',
isShowInfo: false,
// 配置时间选择器默认配置
@@ -939,9 +947,21 @@
});
}
},
+ // 上一项
+ handlePrevItem() {
+ this.currentIndex = Math.max(0, this.currentIndex - 1);
+ this.updateCurrentDetail();
+ },
+
+ // 下一项
+ handleNextItem() {
+ this.currentIndex = Math.min(this.tableData.length - 1, this.currentIndex + 1);
+ this.updateCurrentDetail();
+ },
// 更新当前详情
- updateCurrentDetail() {
+
+ updateCurrentDetail() {
this.currentDetail = this.tableData[this.currentIndex];
this.currentDetail.mediaUrl = this.currentDetail.photo_url;
this.currentDetail.updatePhotoUrl = this.currentDetail.update_photo_url;
@@ -949,10 +969,12 @@
this.currentDetail.showQR = false;
this.currentDetail.latAndLon = _.round(this.currentDetail.location[0], 6) + ',' + _.round(this.currentDetail.location[1], 6);
},
- // 更新当前详情数据
+
+ // 更新当前详情数据
handleCurrentDetailUpdate(updatedDetail) {
this.currentDetail = updatedDetail;
},
+
// 受理并派发
handleApproveAndDispatch(detail) {
this.currentDetail = detail;
@@ -992,64 +1014,7 @@
this.$message.error('地图加载失败,请检查网络或API Key配置');
}
},
- async handleBatchApprove() {
- try {
- if (this.selections.length === 0) {
- this.$message.warning('没有选中的工单');
- return;
- }
- const currentItem = this.selections[this.currentImageIndex - 1];
- if (!currentItem) {
- this.$message.warning('当前工单数据无效');
- return;
- }
-
- await this.$confirm('确认审核通过当前工单?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- });
-
- const data = {
- id: currentItem.id,
- status: currentItem.status,
- isPass: 0,
- eventNum: currentItem.orderNumber,
- eventName: currentItem.orderName,
- };
-
- const response = await flowEvent(data);
- if (response.data.code === 0) {
- this.$message.success('工单审核通过');
-
- // 创建新的数组而不是修改原数组
- const newSelections = [...this.selections];
- newSelections.splice(this.currentImageIndex - 1, 1);
- this.selections = newSelections;
-
- // 修正索引并更新图片
- if (this.selections.length > 0) {
- if (this.currentImageIndex > this.selections.length) {
- this.currentImageIndex = this.selections.length;
- }
- this.updateCurrentReviewImage();
- } else {
- this.reviewDialogVisible = false;
- this.currentImageIndex = 1;
- this.currentReviewImage = '';
- }
-
- // 刷新表格数据
- this.fetchTableData();
- } else {
- throw new Error(response.data.msg || '审核失败');
- }
- } catch (error) {
- if (error === 'cancel') return;
- this.$message.error(error.message || '审核失败,请稍后重试');
- }
- },
async fetchDropdownData() {
try {
const response = await getTicketInfo();
@@ -1334,25 +1299,92 @@
}
return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}`;
},
- handleViewDetail(row) {
+
+ async handleViewDetail(row) {
+ // 找到当前行在tableData中的索引
this.currentIndex = this.tableData.findIndex(item => item.id === row.id);
+ // 先设置workType,直接从row读取
this.workType = row.work_type !== undefined ? Number(row.work_type) : 0;
-
+
+ // 重置上传组件的文件列表
+ this.$nextTick(() => {
+ if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
+ this.$refs.MapContainer.initAddEntity('point', this.currentDetail.location);
+ }
+ });
+
const detailData = {
...row,
processingDetail: row.processing_details || '',
mediaUrl: row.photo_url || row.video_url || '',
updatePhotoUrl: row.update_photo_url || '',
photos: [],
- job_name: row.job_name || '',
- showQR: false,
- latAndLon: _.round(row.location[0], 6) + ',' + _.round(row.location[1], 6),
+ job_name: row.job_name || '', // 新增
};
- this.currentDetail = detailData;
+ let stepArr = [];
+ try {
+ const stepResponse = await getStepInfo(row.orderNumber);
+ const steps = Array.isArray(stepResponse.data.data)
+ ? stepResponse.data.data
+ : stepResponse.data.data?.steps || [];
+ const finishedStep = steps.find(s => String(s.status) === '4');
+ this.totalTime = finishedStep && finishedStep.total_time ? finishedStep.total_time : '';
+ if (this.activeTab !== 'myTickets') {
+ this.stepInfos = steps.map(step => ({
+ status: String(step.status),
+ name: step.name,
+ time: step.time,
+ create_time: step.create_time,
+ }));
+ } else {
+ const statusArr = this.workType === 1 ? ['3', '4'] : this.fixedStatuses;
+ this.stepInfos = statusArr.map(status => {
+ const step = steps.find(s => String(s.status) === String(status));
+ return {
+ status,
+ name: step ? step.name : '',
+ time: step ? step.time : null,
+ create_time: step ? step.create_time : null,
+ };
+ });
+ console.log('this.stepInfos', this.stepInfos);
+
+ }
+ this.currentDetail.status = row.status;
+ } catch (error) {
+ if (this.activeTab === 'myTickets') {
+ const statusArr = this.workType === 1 ? ['3', '4'] : this.fixedStatuses;
+ this.stepInfos = statusArr.map(status => ({
+ status,
+ name: status === row.status ? row.handler || '未分配' : '未处理',
+ time: status === row.status ? row.startTime || '未知时间' : null,
+ }));
+ } else {
+ this.stepInfos = [];
+ }
+ }
+ console.log(detailData, 'detailDatadetailDatadetailData');
+ this.currentDetail = {
+ ...detailData,
+ // address: null,
+ showQR: false,
+ latAndLon: _.round(detailData.location[0], 6) + ',' + _.round(detailData.location[1], 6),
+ };
+
+ console.log('this.currentDetail', this.currentDetail);
+
this.detailVisible = true;
+
+
+
+ this.$nextTick(() => {
+ if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
+ this.$refs.MapContainer.initAddEntity('point', this.currentDetail.location);
+ }
+ });
},
- // 详情成功回调
+ // 详情成功回调
handleDetailSuccess() {
this.fetchTableData();
},
@@ -1361,20 +1393,6 @@
handleDetailError(error) {
console.error('工单操作失败:', error);
},
- // 上一项
- handlePrevItem() {
- this.currentIndex = Math.max(0, this.currentIndex - 1);
- this.updateCurrentDetail();
- },
-
- // 下一项
- handleNextItem() {
- this.currentIndex = Math.min(this.tableData.length - 1, this.currentIndex + 1);
- this.updateCurrentDetail();
- },
-
-
-
getStepHandler(status) {
const step = this.stepInfos.find(step => step.status === status);
return step ? step.name : '';
@@ -1486,8 +1504,57 @@
工单状态: this.mapStatus(Number(item.status || 0)),
};
},
-
-
+ async approveTicket() {
+ if (this.approveLoading) return;
+ this.approveLoading = true;
+ try {
+ const data = {
+ id: this.currentDetail.id,
+ status: this.currentDetail.status,
+ isPass: 0, // 0 表示通过
+ eventNum: this.currentDetail.orderNumber,
+ };
+
+ const file = this.currentDetail.file || null; // 如果没有文件,则为 null
+
+ const response = await flowEvent(data, file);
+ if (response.data.code === 0) {
+ this.$message.success('工单已通过');
+ this.detailVisible = false;
+ this.fetchTableData();
+ } else {
+ throw new Error(response.data.msg || '操作失败');
+ }
+ } catch (error) {
+ this.$message.error(error.message || '操作失败,请稍后重试');
+ } finally {
+ this.approveLoading = false;
+ }
+ },
+ async rejectTicket() {
+ if (this.rejectLoading) return;
+ this.rejectLoading = true;
+ try {
+ const data = {
+ id: this.currentDetail.id,
+ status: this.currentDetail.status,
+ isPass: 1, // 1 表示不通过
+ };
+
+ const response = await flowEvent(data);
+ if (response.data.code === 0) {
+ this.$message.success('工单未通过');
+ this.detailVisible = false;
+ this.fetchTableData();
+ } else {
+ throw new Error(response.data.msg || '操作失败');
+ }
+ } catch (error) {
+ this.$message.error(error.message || '操作失败,请稍后重试');
+ } finally {
+ this.rejectLoading = false;
+ }
+ },
async submitProcessing() {
if (this.currentDetail.status !== 3) {
this.$message.warning('只有处理中状态的工单可以提交处理详情');
@@ -1520,7 +1587,7 @@
markAsCompleted() {
this.$message.success('工单已标记为完成');
},
-
+
async approveAndDispatch() {
if (this.dispatchLoading) return;
@@ -1554,10 +1621,6 @@
// 可以在这里处理错误,或者保持空实现
console.error('派发失败:', error);
},
-
-
-
-
hasProcessingBtnPermission() {
// undefined 或 false 都返回 false,只有 true 返回 true
// console.log('权限检查:', this.permission.tickets_processing_btn)
@@ -1573,8 +1636,6 @@
// console.log('权限检查:', this.permission)
return this.permission && this.permission.tickets_review_btn === true;
},
-
-
async finalizeTicket() {
if (this.finalizeLoading) return;
this.finalizeLoading = true;
@@ -1669,11 +1730,6 @@
// 添加全选方法
handleSelectAll(val) {
this.$refs.avueCrud.toggleSelection(val);
- },
-
- // 添加单行选择方法
- handleSelect(selection, row) {
- console.log('选中行变化:', selection, row);
},
// 如果需要手动选中某些行
@@ -2057,7 +2113,10 @@
margin-bottom: 32px;
}
-
+.tableCss {
+ width: 100%;
+ margin-bottom: 10px;
+}
.step-timer {
position: absolute;
@@ -2070,7 +2129,12 @@
font-size: 12px;
}
-
+.event-total-time {
+ text-align: center;
+ color: #666;
+ font-size: 15px;
+ margin-bottom: 12px;
+}
.action-bar {
margin-bottom: 16px;
@@ -2102,7 +2166,103 @@
margin-top: 12px;
}
+.create-ticket-form {
+ padding: 20px 10px;
+ .form-section {
+ background-color: #fff;
+ border-radius: 4px;
+
+ .el-row {
+ margin-bottom: 16px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ .location-wrapper {
+ width: 100%; // 修改为100%以适应父容器
+ height: auto; // 修改为auto以自适应内容
+
+ .map-button {
+ width: 100%; // 让按钮填满容器宽度
+ height: 36px; // 与其他输入框保持一致的高度
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ i {
+ margin-right: 4px;
+ }
+ }
+
+ .location-text {
+ margin-top: 8px;
+ padding: 8px 12px;
+ background-color: #f5f7fa;
+ border-radius: 4px;
+ color: #606266;
+ font-size: 13px;
+ line-height: 1.4;
+ }
+ }
+
+ .upload-wrapper {
+ .uploader {
+ :deep(.el-upload--picture-card) {
+ width: 120px;
+ height: 100px;
+ line-height: 128px;
+ }
+
+ :deep(.el-upload-list__item) {
+ width: 120px;
+ height: 120px;
+ }
+ }
+
+ .upload-tip {
+ font-size: 12px;
+ color: #909399;
+ line-height: 1.4;
+ margin-top: 8px;
+ }
+ }
+
+ .el-form-item {
+ margin-bottom: 18px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ :deep(.el-form-item__label) {
+ font-weight: 500;
+ color: #606266;
+ }
+
+ // :deep(.el-input__inner) {
+ // height: 36px;
+ // line-height: 36px;
+ // }
+
+ :deep(.el-textarea__inner) {
+ padding: 8px 12px;
+ }
+
+ // :deep(.el-input__inner),
+ // :deep(.el-select),
+ // :deep(.el-select .el-input) {
+ // width: 100%; // 确保所有输入框和选择框宽度一致
+ // }
+
+ .full-width {
+ width: 100%;
+ }
+}
:deep(.el-dialog__body) {
border-top: 0.1rem solid #f0f0f0;
@@ -2244,65 +2404,65 @@
}
}
-// .status-flow {
-// margin-bottom: 20px;
+.status-flow {
+ margin-bottom: 20px;
-// .custom-steps {
-// .el-step__description {
-// position: relative;
-// margin: 0;
-// padding: 0;
-// }
+ .custom-steps {
+ .el-step__description {
+ position: relative;
+ margin: 0;
+ padding: 0;
+ }
-// // 添加发起任务步骤的特殊样式
-// .init-step-info {
-// display: flex;
-// flex-direction: column;
-// align-items: center;
-// text-align: center;
-// width: 130px;
+ // 添加发起任务步骤的特殊样式
+ .init-step-info {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ width: 130px;
-// .creator-name {
-// font-size: 14px;
-// font-weight: bold;
-// color: #303133;
-// margin-bottom: 4px;
-// }
+ .creator-name {
+ font-size: 14px;
+ font-weight: bold;
+ color: #303133;
+ margin-bottom: 4px;
+ }
-// .create-time {
-// font-size: 10px;
-// color: #909399;
-// }
-// }
+ .create-time {
+ font-size: 10px;
+ color: #909399;
+ }
+ }
-// // 保持其他步骤的原有样式
-// .step-info {
-// display: flex;
-// margin: 0;
-// padding: 0;
-// justify-content: space-between;
-// align-items: center;
-// width: 200px;
+ // 保持其他步骤的原有样式
+ .step-info {
+ display: flex;
+ margin: 0;
+ padding: 0;
+ justify-content: space-between;
+ align-items: center;
+ width: 200px;
-// .process-time {
-// font-size: 10px;
-// color: #909399;
-// text-align: left;
-// margin-left: -50%;
-// padding-left: 0%;
-// flex: 1;
-// }
+ .process-time {
+ font-size: 10px;
+ color: #909399;
+ text-align: left;
+ margin-left: -50%;
+ padding-left: 0%;
+ flex: 1;
+ }
-// .handler-name {
-// font-size: 14px;
-// font-weight: bold;
-// color: #303133;
-// text-align: left;
-// flex: 1;
-// }
-// }
-// }
-// }
+ .handler-name {
+ font-size: 14px;
+ font-weight: bold;
+ color: #303133;
+ text-align: left;
+ flex: 1;
+ }
+ }
+ }
+}
.basic-info {
margin-bottom: 20px;
@@ -2336,7 +2496,11 @@
opacity: 0.3 !important;
}
-
+.PopUpTableScrolls {
+ max-height: 600px;
+ overflow-y: scroll;
+ overflow-x: hidden;
+}
.media-box {
width: 100%;
@@ -2421,22 +2585,7 @@
word-break: break-word;
}
-.readonly-processing-detail {
- background-color: #f5f7fa;
- padding: 12px;
- border-radius: 4px;
- min-height: 30px;
- color: #606266;
- line-height: 1.5;
- display: block;
- text-align: left;
- margin-bottom: 5px;
- &:first-child {
- font-weight: bold;
- margin-bottom: 4px;
- }
-}
// 添加删除按钮样式
.danger-button {
@@ -2447,9 +2596,39 @@
color: #f78989;
}
+.custom-steps-container {
+ width: 100%;
+ margin: 10px 0;
+}
+.steps-titles {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 14px;
+ position: relative;
+}
+.step-title {
+ text-align: center;
+ flex: 1;
+ font-size: 14px;
+ color: #999;
+ position: relative;
+ padding-bottom: 5px;
+}
+.step-title.active {
+ color: #409eff;
+ font-weight: bold;
+}
+
+.event-title-center {
+ text-align: center;
+ font-size: 20px;
+ font-weight: bold;
+ margin-bottom: 5px;
+ color: #333;
+}
.custom-steps {
margin-top: -20px;
@@ -2460,7 +2639,18 @@
}
}
+.step-description {
+ font-size: 14px;
+ color: #666;
+ line-height: 1.5;
+ display: block;
+ text-align: center;
+ &:first-child {
+ font-weight: bold;
+ margin-bottom: 4px;
+ }
+}
// 覆盖其他相关样式
.status-flow {
diff --git a/src/views/tickets/ticketComponent/TicketDetailDialog.vue b/src/views/tickets/ticketComponent/TicketDetailDialog.vue
index 9a6a302..dc1acca 100644
--- a/src/views/tickets/ticketComponent/TicketDetailDialog.vue
+++ b/src/views/tickets/ticketComponent/TicketDetailDialog.vue
@@ -585,6 +585,8 @@
getActiveStep() {
const arr = this.stepStatusList;
+ console.log('stepStatusList',this.stepStatusList);
+
const index = arr.indexOf(String(this.currentDetail.status));
return index !== -1 ? index + 2 : 1;
},
@@ -754,14 +756,26 @@
}
};
</script>
-
-<style lang="scss" scoped>
+<style lang="scss">
.custom-dialog {
- max-height: 96vh;
+ max-height: 95vh;
:deep(.el-dialog__body) {
border-top: 0.1rem solid #f0f0f0;
}
+}
+</style>
+<style lang="scss" scoped>
+
+.step-timer {
+ position: absolute;
+ right: 80%;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 100px;
+ margin-left: 4px;
+ color: #666;
+ font-size: 12px;
}
.detail-container {
padding: 0 20px;
@@ -1061,4 +1075,20 @@
pointer-events: none;
opacity: 0.3 !important;
}
+.readonly-processing-detail {
+ background-color: #f5f7fa;
+ padding: 12px;
+ border-radius: 4px;
+ min-height: 30px;
+ color: #606266;
+ line-height: 1.5;
+ display: block;
+ text-align: left;
+ margin-bottom: 5px;
+
+ &:first-child {
+ font-weight: bold;
+ margin-bottom: 4px;
+ }
+}
</style>
\ No newline at end of file
--
Gitblit v1.9.3