瀏覽代碼

reactor:对接呼叫中心话机动作统计;

zhangchong 9 月之前
父節點
當前提交
6be570d2bb

+ 2 - 2
.env.development

@@ -3,11 +3,11 @@ VITE_MODE_NAME=development
 # 防止部署多套系统到同一域名不同目录时,变量共用的问题 设置不同的前缀
 VITE_STORAGE_NAME=dev
 # 基础请求地址
-VITE_API_URL=http://110.188.24.28:50100
+VITE_API_URL=http://110.188.24.28:50300
 # 数据共享平台请求地址
 VITE_DATASHARE_API_YRL=http://ds.12345lm.cn
 # socket API
-VITE_API_SOCKET_URL=http://110.188.24.28:50100/hubs/hotline
+VITE_API_SOCKET_URL=http://110.188.24.28:50300/hubs/hotline
 # 上传 API
 VITE_API_UPLOAD_URL=http://110.188.24.28:50120
 # 文件上传地址前缀

+ 11 - 0
src/api/callCenter/index.ts

@@ -89,4 +89,15 @@ export const getCallCenterCallRecord = (params?: object) => {
         method: 'get',
         params,
     });
+};
+/**
+ * @description 查询呼叫中心坐席操作记录
+ * @param params
+ */
+export const getCallCenterOperateRecord = (params?: object) => {
+    return request({
+        url: `/api/v1/Call/tel-operations-fixed`,
+        method: 'get',
+        params,
+    });
 };

+ 273 - 283
src/components/ProcessAudit/index.vue

@@ -422,13 +422,13 @@
 								<annex-list :name="state.annexName" :businessId="state.orderDetail.id" :classify="state.classify" v-model:format="handleFiles" />
 							</el-form-item>
 						</el-col>
-						<el-col v-if="state.dialogTitle === '工单受理'" :span="24">
-							<el-form-item>
-								<template #label>
-									<span class="color-danger">注意</span>
-								</template>
-								<span class="color-danger">打开办理弹窗后如有修改工单信息,请关闭本弹窗后重新打开!</span>
-							</el-form-item>
+						<el-col v-if="state.dialogTitle === '工单受理'" :span="24" :class="{ mb20: isOverdueTips }">
+							<el-alert type="warning" show-icon title="注意">
+								<template #title> 打开办理弹窗后如有修改工单信息,请关闭本弹窗后重新打开 </template>
+							</el-alert>
+						</el-col>
+						<el-col v-if="isOverdueTips" :span="24">
+							<el-alert type="warning" show-icon title="注意"> 该工单属于超期状态,若符合延期要求,请延期通过后办结,是否继续办理 </el-alert>
 						</el-col>
 					</template>
 				</template>
@@ -706,6 +706,10 @@ watchEffect(() => {
 // 流程选择下一环节
 const fastStepCode = ref(''); // 推荐派单处理对象code
 const fastStepName = ref(''); // 推荐派单处理对象
+// expiredStatus 超期状态(0正常 1即将超期  2已超期)  工单流转选择“结束”节点,点击“办理”时需验证该工单是否处于已超期状态
+const isOverdueTips = computed(() => {
+	return state.orderDetail.expiredStatus === 2 && state.ruleForm.nextStepCode === 'end';
+});
 const selectNextStep = (val: any) => {
 	ruleFormRef.value?.resetFields('nextHandlers');
 	ruleFormRef.value?.resetFields('nextMainHandler');
@@ -727,7 +731,6 @@ const selectNextStep = (val: any) => {
 	});
 	fastStepName.value = next.recommendOrgName; // 推荐派单处理对象
 	fastStepCode.value = next.recommendOrgId; // 推荐派单处理对象code
-
 	if (items.length === 1) {
 		// 如果办理对象只有一个默认选中
 		state.ruleForm.nextHandlers = [items[0]];
@@ -958,288 +961,275 @@ const onSubmit = (formEl: FormInstance | undefined) => {
 		if (!valid) return;
 		let isAuditText = '确认办理';
 		// expiredStatus 超期状态(0正常 1即将超期  2已超期)  工单流转选择“结束”节点,点击“办理”时需验证该工单是否处于已超期状态
-		if (state.orderDetail.expiredStatus === 2 && state.ruleForm.nextStepCode === 'end') {
-			isAuditText = '该工单属于超期状态,若符合延期要求,请延期通过后办结,是否继续办理';
+
+		state.loading = true;
+		let submitObj = other.deepClone(state.ruleForm);
+		if (submitObj.nextHandlers && submitObj.nextHandlers.length) {
+			if (submitObj.nextHandlers.length === 1) {
+				submitObj.nextMainHandler = submitObj.nextHandlers[0].key;
+			}
 		}
-		ElMessageBox.confirm(`${isAuditText}?`, '提示', {
-			confirmButtonText: '确认',
-			cancelButtonText: '取消',
-			type: 'warning',
-			draggable: true,
-			cancelButtonClass: 'default-button',
-			autofocus: false,
-		})
-			.then(() => {
-				state.loading = true;
-				let submitObj = other.deepClone(state.ruleForm);
-				if (submitObj.nextHandlers && submitObj.nextHandlers.length) {
-					if (submitObj.nextHandlers.length === 1) {
-						submitObj.nextMainHandler = submitObj.nextHandlers[0].key;
-					}
+		if (!flowDirection.value) {
+			// 需要填写办理时限
+			Reflect.deleteProperty(submitObj, 'timeLimit');
+			Reflect.deleteProperty(submitObj, 'timeLimitUnit');
+		} else {
+			submitObj.external = {
+				timeLimit: state.ruleForm.timeLimit,
+				timeLimitUnit: state.ruleForm.timeLimitUnit,
+			};
+		}
+		Reflect.deleteProperty(submitObj, 'isPoliceReturn');
+		Reflect.deleteProperty(submitObj, 'isResolved');
+		submitObj.external = {
+			isPoliceReturn: state.ruleForm.isPoliceReturn,
+			isResolved: state.ruleForm.isResolved,
+		};
+		// submitObj.stepExpiredTime = submitObj.expiredTime; //节点过期时间
+		switch (state.processType) {
+			case '工单受理':
+				const request = {
+					data: { ...state.orderDetail },
+					workflow: { ...submitObj, files: handleFiles.value },
+				};
+				orderStartFlow(request)
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '工单撤回':
+				workflowRecall({ ...submitObj, files: handleFiles.value })
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '延期申请':
+				const requestDelay = {
+					data: {
+						orderId: state.orderDetail.id,
+						delayNum: state.delayForm.timeLimitCount,
+						delayUnit: state.delayForm.timeLimitUnit,
+						delayReason: state.delayForm.content,
+						files: handleFilesDelay.value,
+					},
+					workflow: {
+						...submitObj,
+						opinion: state.delayForm.content,
+						files: handleFilesDelay.value,
+					},
+				};
+				delayApply(requestDelay)
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true, '延期申请成功');
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '延期退回':
+				workflowPrevious({ ...submitObj, files: handleFiles.value })
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '延期审批':
+				if (state.ruleForm.isPass) {
+					// 审批通过 下一步
+					workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
+						.then(() => {
+							afterSubmit('orderProcessSuccess', true);
+						})
+						.catch(() => {
+							afterSubmit('orderProcessFailed');
+						});
+				} else {
+					// 审批拒绝
+					const requestDelayAudit = {
+						opinion: state.ruleForm.opinion,
+						workflowId: state.workflowId,
+						files: handleFiles.value,
+					};
+					workflowReject(requestDelayAudit)
+						.then(() => {
+							afterSubmit('orderProcessSuccess', true);
+						})
+						.catch(() => {
+							afterSubmit('orderProcessFailed');
+						});
 				}
-				if (!flowDirection.value) {
-					// 需要填写办理时限
-					Reflect.deleteProperty(submitObj, 'timeLimit');
-					Reflect.deleteProperty(submitObj, 'timeLimitUnit');
+				break;
+			case '甄别申请':
+				const requestDiscern = {
+					data: {
+						no: state.orderDetail.no,
+						visitId: state.orderDetail.visitId,
+						visitDetailId: state.orderDetail.visitDetailId,
+						orderId: state.orderDetail.id,
+						typeDicId: state.discernForm.type.dicDataValue,
+						typeDicName: state.discernForm.type.dicDataName,
+						content: state.discernForm.content,
+						files: handleFilesDiscern.value,
+					},
+					workflow: { ...submitObj, files: handleFilesDiscern.value, opinion: state.discernForm.content },
+				};
+				discernApply(requestDiscern)
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true, '甄别申请成功');
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '甄别退回':
+				workflowPrevious({ ...submitObj, files: handleFiles.value })
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '甄别审批':
+				if (state.ruleForm.isPass) {
+					// 审批通过 下一步
+					workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
+						.then(() => {
+							afterSubmit('orderProcessSuccess', true);
+						})
+						.catch(() => {
+							afterSubmit('orderProcessFailed');
+						});
 				} else {
-					submitObj.external = {
-						timeLimit: state.ruleForm.timeLimit,
-						timeLimitUnit: state.ruleForm.timeLimitUnit,
+					// 审批拒绝
+					const requestDiscernAudit = {
+						opinion: state.ruleForm.opinion,
+						workflowId: state.workflowId,
+						files: handleFiles.value,
 					};
+					workflowReject(requestDiscernAudit)
+						.then(() => {
+							afterSubmit('orderProcessSuccess', true);
+						})
+						.catch(() => {
+							afterSubmit('orderProcessFailed');
+						});
 				}
-				Reflect.deleteProperty(submitObj, 'isPoliceReturn');
-				Reflect.deleteProperty(submitObj, 'isResolved');
-				submitObj.external = {
-					isPoliceReturn: state.ruleForm.isPoliceReturn,
-					isResolved: state.ruleForm.isResolved,
+				break;
+			case '工单退回':
+				orderPrevious({ ...submitObj, files: handleFiles.value })
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true, '退回申请成功');
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '工单办理': // 工单办理流程
+				orderHandle({ ...submitObj, files: handleFiles.value })
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '新增知识':
+				const KnowledgeAddRequest = {
+					data: { ...state.orderDetail },
+					workflow: { ...submitObj, files: handleFiles.value },
 				};
-				// submitObj.stepExpiredTime = submitObj.expiredTime; //节点过期时间
-				switch (state.processType) {
-					case '工单受理':
-						const request = {
-							data: { ...state.orderDetail },
-							workflow: { ...submitObj, files: handleFiles.value },
-						};
-						orderStartFlow(request)
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '工单撤回':
-						workflowRecall({ ...submitObj, files: handleFiles.value })
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '延期申请':
-						const requestDelay = {
-							data: {
-								orderId: state.orderDetail.id,
-								delayNum: state.delayForm.timeLimitCount,
-								delayUnit: state.delayForm.timeLimitUnit,
-								delayReason: state.delayForm.content,
-								files: handleFilesDelay.value,
-							},
-							workflow: {
-								...submitObj,
-								opinion: state.delayForm.content,
-								files: handleFilesDelay.value,
-							},
-						};
-						delayApply(requestDelay)
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true, '延期申请成功');
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '延期退回':
-						workflowPrevious({ ...submitObj, files: handleFiles.value })
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '延期审批':
-						if (state.ruleForm.isPass) {
-							// 审批通过 下一步
-							workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
-								.then(() => {
-									afterSubmit('orderProcessSuccess', true);
-								})
-								.catch(() => {
-									afterSubmit('orderProcessFailed');
-								});
-						} else {
-							// 审批拒绝
-							const requestDelayAudit = {
-								opinion: state.ruleForm.opinion,
-								workflowId: state.workflowId,
-								files: handleFiles.value,
-							};
-							workflowReject(requestDelayAudit)
-								.then(() => {
-									afterSubmit('orderProcessSuccess', true);
-								})
-								.catch(() => {
-									afterSubmit('orderProcessFailed');
-								});
-						}
-						break;
-					case '甄别申请':
-						const requestDiscern = {
-							data: {
-								no: state.orderDetail.no,
-								visitId: state.orderDetail.visitId,
-								visitDetailId: state.orderDetail.visitDetailId,
-								orderId: state.orderDetail.id,
-								typeDicId: state.discernForm.type.dicDataValue,
-								typeDicName: state.discernForm.type.dicDataName,
-								content: state.discernForm.content,
-								files: handleFilesDiscern.value,
-							},
-							workflow: { ...submitObj, files: handleFilesDiscern.value, opinion: state.discernForm.content },
-						};
-						discernApply(requestDiscern)
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true, '甄别申请成功');
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '甄别退回':
-						workflowPrevious({ ...submitObj, files: handleFiles.value })
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '甄别审批':
-						if (state.ruleForm.isPass) {
-							// 审批通过 下一步
-							workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
-								.then(() => {
-									afterSubmit('orderProcessSuccess', true);
-								})
-								.catch(() => {
-									afterSubmit('orderProcessFailed');
-								});
-						} else {
-							// 审批拒绝
-							const requestDiscernAudit = {
-								opinion: state.ruleForm.opinion,
-								workflowId: state.workflowId,
-								files: handleFiles.value,
-							};
-							workflowReject(requestDiscernAudit)
-								.then(() => {
-									afterSubmit('orderProcessSuccess', true);
-								})
-								.catch(() => {
-									afterSubmit('orderProcessFailed');
-								});
-						}
-						break;
-					case '工单退回':
-						orderPrevious({ ...submitObj, files: handleFiles.value })
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true, '退回申请成功');
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '工单办理': // 工单办理流程
-						orderHandle({ ...submitObj, files: handleFiles.value })
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '新增知识':
-						const KnowledgeAddRequest = {
-							data: { ...state.orderDetail },
-							workflow: { ...submitObj, files: handleFiles.value },
-						};
-						KnowledgeAdd(KnowledgeAddRequest)
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true, '新增知识成功');
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '知识审批':
-						if (state.ruleForm.isPass) {
-							// 审批通过 下一步
-							workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
-								.then(() => {
-									afterSubmit('orderProcessSuccess', true);
-								})
-								.catch(() => {
-									afterSubmit('orderProcessFailed');
-								});
-						} else {
-							// 审批拒绝
-							const requestDiscernAudit = {
-								opinion: state.ruleForm.opinion,
-								workflowId: state.workflowId,
-								files: handleFiles.value,
-							};
-							workflowReject(requestDiscernAudit)
-								.then(() => {
-									afterSubmit('orderProcessSuccess', true);
-								})
-								.catch(() => {
-									afterSubmit('orderProcessFailed');
-								});
-						}
-						break;
-					case '更新新增知识':
-						const KnowledgeAddUpdateRequest = {
-							data: { ...state.orderDetail },
-							workflow: { ...submitObj, files: handleFiles.value },
-						};
-						KnowledgeUpdate(KnowledgeAddUpdateRequest)
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '更新知识':
-						const KnowledgeUpdateRequest = {
-							data: { ...state.orderDetail },
-							workflow: { ...submitObj, files: handleFiles.value },
-						};
-						KnowledgeUpdate(KnowledgeUpdateRequest)
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					case '删除知识':
-						const KnowledgeRemoveRequest = {
-							data: { ...state.orderDetail },
-							workflow: { ...submitObj, files: handleFiles.value },
-						};
-						KnowledgeDel(KnowledgeRemoveRequest)
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true, '删除知识申请成功');
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
-					default: // 默认工单办理
-						orderHandle({ ...submitObj, files: handleFiles.value })
-							.then(() => {
-								afterSubmit('orderProcessSuccess', true);
-							})
-							.catch(() => {
-								afterSubmit('orderProcessFailed');
-							});
-						break;
+				KnowledgeAdd(KnowledgeAddRequest)
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true, '新增知识成功');
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '知识审批':
+				if (state.ruleForm.isPass) {
+					// 审批通过 下一步
+					workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
+						.then(() => {
+							afterSubmit('orderProcessSuccess', true);
+						})
+						.catch(() => {
+							afterSubmit('orderProcessFailed');
+						});
+				} else {
+					// 审批拒绝
+					const requestDiscernAudit = {
+						opinion: state.ruleForm.opinion,
+						workflowId: state.workflowId,
+						files: handleFiles.value,
+					};
+					workflowReject(requestDiscernAudit)
+						.then(() => {
+							afterSubmit('orderProcessSuccess', true);
+						})
+						.catch(() => {
+							afterSubmit('orderProcessFailed');
+						});
 				}
-			})
-			.catch(() => {});
+				break;
+			case '更新新增知识':
+				const KnowledgeAddUpdateRequest = {
+					data: { ...state.orderDetail },
+					workflow: { ...submitObj, files: handleFiles.value },
+				};
+				KnowledgeUpdate(KnowledgeAddUpdateRequest)
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '更新知识':
+				const KnowledgeUpdateRequest = {
+					data: { ...state.orderDetail },
+					workflow: { ...submitObj, files: handleFiles.value },
+				};
+				KnowledgeUpdate(KnowledgeUpdateRequest)
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			case '删除知识':
+				const KnowledgeRemoveRequest = {
+					data: { ...state.orderDetail },
+					workflow: { ...submitObj, files: handleFiles.value },
+				};
+				KnowledgeDel(KnowledgeRemoveRequest)
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true, '删除知识申请成功');
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+			default: // 默认工单办理
+				orderHandle({ ...submitObj, files: handleFiles.value })
+					.then(() => {
+						afterSubmit('orderProcessSuccess', true);
+					})
+					.catch(() => {
+						afterSubmit('orderProcessFailed');
+					});
+				break;
+		}
 	});
 };
 // 暴露变量

+ 2 - 5
src/layout/footer/index.vue

@@ -22,7 +22,7 @@ const props = defineProps({
 	},
 	type: {
 		type: String,
-		default: 'default',
+		default: 'primary',
 	},
 });
 </script>
@@ -31,10 +31,7 @@ const props = defineProps({
 .layout-footer {
 	width: 100%;
 	padding: 5px 0;
-	color:var(--el-text-color-primary);
-  :deep(.el-link){
-    color:var(--el-text-color-primary);
-  }
+	color: var(--el-text-color-primary);
 	&-warp {
 		display: flex;
 		align-items: center;

+ 19 - 14
src/layout/navBars/breadcrumb/ybTel.vue

@@ -543,7 +543,7 @@ import { getDataByCode } from '@/api/system/dict';
 import { useTransition, useDocumentVisibility } from '@vueuse/core';
 import { useWebSocket } from '@/hooks/useWebsocket';
 import { olaFn } from '@/utils/olaFn';
-import { useIntervalFn } from '@vueuse/shared/index';
+import {useIntervalFn, useTimeoutFn} from '@vueuse/shared/index';
 import { callCenterIsSignIn, callCenterWs } from '@/utils/callCenter';
 // 引入组件
 const CommonAdvice = defineAsyncComponent(() => import('@/components/CommonAdvice/index.vue')); // 常用意见
@@ -1048,16 +1048,21 @@ const onMessage = async (event: any) => {
 					useTelStatusStore.setTalkingDeal(true);
 					// 设置话机状态 设置为话后整理中
 					useTelStatusStore.setPhoneControlState(TelStates.onTalkingDeal);
-					talkDealTimer.value = setTimeout(() => {
-						// 设置话后整理
-						useTelStatusStore.setTalkingDeal(false);
-						// 设置话机状态 取消话后整理修改为空闲状态
-						useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
-						olaRef.value.go_ready(); // 示闲
-						console.log('呼叫中心:调用示闲', getNowDateTime());
-						onEndAcw(); // 挂机后整理结束
-						clearTimeout(talkDealTimer.value); // 清除话后整理定时器
-					}, time);
+
+          talkDealTimer.value =  useTimeoutFn(
+              () => {
+                console.log('1111')
+                // 设置话后整理
+                useTelStatusStore.setTalkingDeal(false);
+                // 设置话机状态 取消话后整理修改为空闲状态
+                useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
+                olaRef.value.go_ready(); // 示闲
+                console.log('呼叫中心:调用示闲', getNowDateTime());
+                onEndAcw(); // 挂机后整理结束
+                talkDealTimer.value.stop();
+              },
+              time,
+          );
 
 					console.log('呼叫中心:话后整理中', getNowDateTime());
 					// 如果不是话后整理中
@@ -1642,7 +1647,7 @@ const clickOnRest = (formEl: FormInstance | undefined) => {
 		} else {
 			//不需要审核直接开始小休
 			olaRef.value.go_break(state.restForm.reason); //设置忙碌
-			clearTimeout(talkDealTimer.value); // 清除话后整理定时器
+      talkDealTimer.value.stop();
 			console.log('呼叫中心:调用示忙', getNowDateTime());
 			state.restDialogVisible = false;
 			state.loading = false;
@@ -1793,7 +1798,7 @@ const unTalkingDeal = () => {
 			// 设置话机状态 取消话后整理修改为空闲状态
 			useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
 			olaRef.value.go_ready(); // 示闲
-			clearTimeout(talkDealTimer.value); // 清除话后整理定时器
+      talkDealTimer.value.stop();
 			onEndAcw(); // 挂机后整理结束
 			isAcw.value = false;
 			console.log('呼叫中心:调用示闲', getNowDateTime());
@@ -2005,7 +2010,7 @@ const resetState = () => {
 	useTelStatusStore.resetState();
 	stopSignTime(); // 移除签入时长定时器
 	stopTalkTimer(); // 移除通话计时器
-	clearTimeout(talkDealTimer.value); // 清除话后整理定时器
+  talkDealTimer.value.stop();
 	state.loading = false;
 };
 // 获取当前分机状态

+ 9 - 3
src/layout/navBars/breadcrumb/zgTel.vue

@@ -131,7 +131,7 @@ import { getNowDateTime } from '@/utils/constants';
 import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
 import { useRouter } from 'vue-router';
 import { useWebSocket } from '@/hooks/useWebsocket';
-import { useIntervalFn } from '@vueuse/shared';
+import { useIntervalFn,useTimeoutFn } from '@vueuse/shared';
 import { formatDuration } from '@/utils/formatTime';
 import { Local } from '@/utils/storage';
 import { useAppConfig } from '@/stores/appConfig';
@@ -139,8 +139,14 @@ import { storeToRefs } from 'pinia';
 import { useUserInfo } from '@/stores/userInfo';
 import { callCenterIsSignIn, callCenterWs, currentTel } from '@/utils/callCenter';
 import mittBus from '@/utils/mitt';
-import { callCenterSignIn, callCenterSignOut, callCenterSignOutForce, getCallCenterStatus } from '@/api/callCenter';
-
+import { callCenterSignIn, callCenterSignOut, getCallCenterStatus } from '@/api/callCenter';
+const a = useTimeoutFn(
+    () => {
+      console.log('1111')
+    },
+    5000,
+);
+console.log(a)
 const state = reactive({
 	dutyDialogVisible: false,
 	loading: false,

+ 3 - 3
src/theme/app.scss

@@ -35,9 +35,9 @@
 	--hotline-tagsview-icon-color: #d4d4d4;
 
 	// element主题色
-	--el-color-danger: #f41e1e;
-	--el-color-success: #34d367;
-	--el-color-warning: #ffbb32;
+	--el-color-danger: #f56c6c;
+	--el-color-success: #67c23a;
+	--el-color-warning: #e6a23c;
 
 	// 工单流转记录
 	--hotline-order-CrculationRecord-color: #eef0f4;

+ 7 - 0
src/theme/element.scss

@@ -35,9 +35,16 @@
 ------------------------------- */
 .el-alert {
 	border: 1px solid;
+	.el-alert__description{
+		font-size: var(--el-font-size-base);
+	}
+	.el-alert__icon{
+		font-size: var(--el-font-size-large);
+	}
 }
 .el-alert__title {
 	word-break: break-all;
+	font-size: var(--el-font-size-medium);
 }
 
 /* Message 消息提示

+ 49 - 318
src/views/tels/callLog/zgCallLog.vue

@@ -1,10 +1,11 @@
 <template>
 	<div class="tels-callLog-container layout-pd">
 		<el-card shadow="never">
-			<el-tabs v-model="state.queryParams.IsConnected" @tab-change="changeTba">
-				<el-tab-pane name="" label="全部"></el-tab-pane>
-				<el-tab-pane name="true" label="已接通"></el-tab-pane>
-				<el-tab-pane name="false" label="未接"></el-tab-pane>
+			<el-tabs v-model="state.queryParams.type" @tab-change="changeTba">
+<!--				<el-tab-pane name="0" label="全部"></el-tab-pane>-->
+				<el-tab-pane name="1" label="呼入列表"></el-tab-pane>
+				<el-tab-pane name="2" label="呼出列表"></el-tab-pane>
+				<el-tab-pane name="3" label="未接列表"></el-tab-pane>
 			</el-tabs>
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent label-width="20px">
 				<el-row :gutter="10">
@@ -40,7 +41,7 @@
 					<transition name="el-zoom-in-top">
 						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol || ['4'].includes(state.queryParams.type)">
 							<el-form-item prop="EndBy">
-								<el-select v-model="state.queryParams.EndBy" placeholder="挂机类型" clearable class="w100" @change="handleQuery">
+								<el-select v-model="state.queryParams.EndBy" placeholder="挂断状态" clearable class="w100" @change="handleQuery">
 									<el-option v-for="item in state.endByOptions" :value="item.key" :key="item.key" :label="item.value" />
 								</el-select>
 							</el-form-item>
@@ -54,8 +55,8 @@
 									type="datetimerange"
 									unlink-panels
 									range-separator="至"
-									start-placeholder="呼入时间"
-									end-placeholder="结束事件"
+									start-placeholder="开始时间"
+									end-placeholder="挂断时间"
 									:shortcuts="shortcuts"
 									@change="handleQuery"
 									value-format="YYYY-MM-DD[T]HH:mm:ss"
@@ -145,10 +146,8 @@ import { defineAsyncComponent, onMounted, reactive, ref, onActivated, onBeforeUn
 import type { FormInstance } from 'element-plus';
 import { ElMessageBox } from 'element-plus';
 import { downloadFileByStream, getNeedArr } from '@/utils/tools';
-import { callBaseData, callLogPaged } from '@/api/tels/callLog';
 import { formatDate } from '@/utils/formatTime';
 import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
-import other from '@/utils/other';
 import { useRouter } from 'vue-router';
 import { fileDownload } from '@/api/public/file';
 import mittBus from '@/utils/mitt';
@@ -168,7 +167,8 @@ const state = reactive<any>({
 	queryParams: {
 		PageIndex: 1, // 当前页
 		PageSize: 10, // 每页条数
-		IsConnected: '', // 接通状态
+		type: '1', // 默认全部
+		IsConnected: null, // 接通状态
 		FromNo: null, // 主叫
 		ToNo: null, // 被叫
 		UserName: null, // 话务员名称
@@ -198,341 +198,87 @@ const allColumns = [
 	{ prop: 'toNo', label: '被叫号码', width: 120 },
 	{ prop: 'order.no', label: '工单编码', width: 150 },
 	{ prop: 'order.title', label: '工单标题', width: 200 },
-	{ prop: 'telNo', label: '响应分机', width: 120 },
+  { prop: 'telNo', label: '响应分机' },
 	{ prop: 'mobileAreaName', label: '号码归属地', width: 120 },
 	{ prop: 'userName', label: '话务员', width: 120 },
-	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
+  { prop: 'groupId', label: '功能组号码', width: 120 },
+	{ prop: 'duration', label: '通话时长(秒)', width: 120 },
 	{ prop: 'onStateText', label: '通话结果' },
 	{ prop: 'directionText', label: '电话方向' },
-	{ prop: 'hangupBy', label: '挂机类型', width: 120 },
-	{
-		prop: 'beginIvrTime',
-		label: 'ivr开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endIvrTime',
-		label: 'ivr结束时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'beginQueueTime',
-		label: '队列开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endQueueTime',
-		label: '队列结束时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'beginRingTime',
-		label: '开始振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.beginRingTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'endRingTimg',
-		label: '结束振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endRingTimg, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'createdTime',
-		label: '开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
+	{ prop: 'hangupBy', label: '挂断状态', width: 120 },
 	{
 		prop: 'answeredTime',
 		label: '接通时间',
-		width: 170,
+		width: 160,
 		render: (scope) => <span>{formatDate(scope.row.answeredTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
 	},
-	{ prop: 'overTime', label: '挂断时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'overTime', label: '挂断时间', width: 160, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
 	{ prop: 'operation', label: '操作', fixed: 'right', width: 160, align: 'center' },
 ];
-// 呼入已接表头
+// 呼入列表表头(已接通)
 const inColumns = [
-	{ prop: 'cpn', label: '主叫号码', width: 120 },
-	{ prop: 'cdpn', label: '被叫号码', width: 120 },
+	{ prop: 'fromNo', label: '主叫号码', width: 120 },
+	{ prop: 'toNo', label: '被叫号码', width: 120 },
 	{ prop: 'order.no', label: '工单编码', width: 150 },
-	{ prop: 'title', label: '工单标题', width: 300 },
-	{ prop: 'telNo', label: '响应分机', width: 120 },
+	{ prop: 'order.title', label: '工单标题', width: 300 },
+	{ prop: 'telNo', label: '响应分机' },
 	{ prop: 'mobileAreaName', label: '号码归属地', width: 120 },
 	{ prop: 'userName', label: '话务员', width: 120 },
+  { prop: 'groupId', label: '功能组号码', width: 120 },
 	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
-	{ prop: 'hangupBy', label: '挂机类型', width: 120 },
-	{
-		prop: 'beginIvrTime',
-		label: 'ivr开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endIvrTime',
-		label: 'ivr结束时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'beginQueueTime',
-		label: '队列开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endQueueTime',
-		label: '队列结束时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'beginRingTime',
-		label: '开始振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.beginRingTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'endRingTimg',
-		label: '结束振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endRingTimg, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
+	{ prop: 'hangupBy', label: '挂断状态', width: 120 },
 	{
 		prop: 'createdTime',
 		label: '开始时间',
-		width: 170,
+		width: 160,
 		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
 	},
 	{
 		prop: 'answeredTime',
 		label: '接通时间',
-		width: 170,
+		width: 160,
 		render: (scope) => <span>{formatDate(scope.row.answeredTime, ' YYYY-mm-dd HH:MM:SS')}</span>,
 	},
-	{ prop: 'overTime', label: '挂断结束时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'overTime', label: '挂断时间', width: 160, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
 	{ prop: 'operation', label: '操作', fixed: 'right', width: 310, align: 'center' },
 ];
-// 呼出已接表头
+// 呼出列表表头(已接通)
 const outColumns = [
-	{ prop: 'cpn', label: '主叫号码', width: 120 },
-	{ prop: 'cdpn', label: '被叫号码', width: 120 },
+	{ prop: 'fromNo', label: '主叫号码', width: 120 },
+	{ prop: 'toNo', label: '被叫号码', width: 120 },
 	{ prop: 'telNo', label: '响应分机' },
-	{ prop: 'gateway', label: '中继号码', width: 120 },
-	{ prop: 'userName', label: '话务员' },
+  { prop: 'hangupBy', label: '挂断状态', width: 120 },
+  { prop: 'groupId', label: '功能组号码', width: 120 },
 	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
-	{ prop: 'endByText', label: '挂机类型', width: 120 },
 	{
 		prop: 'createdTime',
 		label: '开始时间',
-		width: 170,
+		width: 160,
 		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
 	},
 	{
 		prop: 'answeredTime',
 		label: '接通时间',
-		width: 170,
+		width: 160,
 		render: (scope) => <span>{formatDate(scope.row.answeredTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
 	},
-	{ prop: 'overTime', label: '挂断时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'overTime', label: '挂断时间', width: 160, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
 	{ prop: 'operation', label: '操作', fixed: 'right', width: 160, align: 'center' },
 ];
 // 未接表头
 const noColumns = [
-	{ prop: 'cpn', label: '主叫号码', width: 120 },
+  { prop: 'callDirectionText', label: '呼叫方向' },
+	{ prop: 'fromNo', label: '主叫号码', width: 120 },
 	{ prop: 'cdpn', label: '被叫号码', width: 120 },
 	{ prop: 'telNo', label: '响应分机' },
-	{ prop: 'gateway', label: '中继号码', width: 120 },
-	{ prop: 'mobileAreaName', label: '号码归属地', width: 120 },
-	{ prop: 'userName', label: '话务员' },
-	{ prop: 'callDirectionText', label: '电话方向' },
-	{
-		prop: 'beginIvrTime',
-		label: 'ivr开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endIvrTime',
-		label: 'ivr结束时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'beginQueueTime',
-		label: '队列开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endQueueTime',
-		label: '队列结束时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'beginRingTime',
-		label: '开始振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.beginRingTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'endRingTimg',
-		label: '结束振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endRingTimg, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
+  { prop: 'groupId', label: '功能组号码', width: 120 },
 	{
 		prop: 'createdTime',
 		label: '开始时间',
-		width: 170,
+		width: 160,
 		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
 	},
-	{ prop: 'overTime', label: '挂断时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
-];
-// 智能应答
-const smartColumns = [
-	{ prop: 'cpn', label: '主叫号码', width: 120 },
-	{ prop: 'cdpn', label: '被叫号码', width: 120 },
-	{ prop: 'gateway', label: '中继号码', width: 120 },
-	{ prop: 'mobileAreaName', label: '号码归属地', width: 120 },
-	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
-	{ prop: 'endByText', label: '挂机类型', width: 120 },
-	{
-		prop: 'beginIvrTime',
-		label: 'ivr开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endIvrTime',
-		label: 'ivr结束时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'createdTime',
-		label: '开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'answeredTime',
-		label: '接通时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.answeredTime, ' YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{ prop: 'overTime', label: '挂断结束时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
-	{ prop: 'operation', label: '操作', fixed: 'right', width: 160, align: 'center' },
-];
-// 呼入白名单表头
-const whiteColumns = [
-	{ prop: 'cpn', label: '主叫号码', width: 120 },
-	{ prop: 'cdpn', label: '被叫号码', width: 120 },
-	{ prop: 'telNo', label: '响应分机', width: 120 },
-	{ prop: 'gateway', label: '中继号码', width: 120 },
-	{ prop: 'mobileAreaName', label: '号码归属地', width: 120 },
-	{ prop: 'userName', label: '话务员', width: 120 },
-	{ prop: 'duration', label: '通话时间(秒)', width: 120 },
-	{ prop: 'onStateText', label: '通话结果' },
-	{ prop: 'callDirectionText', label: '电话方向' },
-	{ prop: 'endByText', label: '挂机类型', width: 120 },
-	{
-		prop: 'beginIvrTime',
-		label: 'ivr开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endIvrTime',
-		label: 'ivr结束时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.endIvrTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'beginQueueTime',
-		label: '队列开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.beginQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'endQueueTime',
-		label: '队列结束时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endQueueTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'beginRingTime',
-		label: '开始振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.beginRingTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'endRingTimg',
-		label: '结束振铃时间',
-		width: 170,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.endRingTimg, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
-	{
-		prop: 'createdTime',
-		label: '开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{
-		prop: 'answeredTime',
-		label: '接通时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.answeredTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{ prop: 'overTime', label: '挂断时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
-	{ prop: 'operation', label: '操作', fixed: 'right', width: 160, align: 'center' },
-];
-// 呼入黑名单表头
-const blackColumns = [
-	{ prop: 'cpn', label: '主叫号码', minWidth: 120 },
-	{ prop: 'cdpn', label: '被叫号码', minWidth: 120 },
-	{ prop: 'telNo', label: '响应分机', minWidth: 120 },
-	{ prop: 'gateway', label: '中继号码', minWidth: 120 },
-	{ prop: 'mobileAreaName', label: '号码归属地', minWidth: 120 },
-	{ prop: 'duration', label: '通话时间(秒)', minWidth: 120 },
-	{ prop: 'endByText', label: '挂机类型', minWidth: 120 },
-	{
-		prop: 'createdTime',
-		label: '开始时间',
-		width: 170,
-		render: (scope) => <span>{formatDate(scope.row.createdTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
-	},
-	{ prop: 'overTime', label: '挂断结束时间', width: 170, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'overTime', label: '挂断时间', width: 160, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
 ];
 const changeTba = () => {
 	ruleFormRef.value.resetFields();
@@ -569,38 +315,23 @@ const queryList = async (isQuery: boolean = false) => {
 	requestParams.value.CallStartTimeLT = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
 	Reflect.deleteProperty(requestParams.value, 'crTime'); // 通话开始和结束时间段
 
-	switch (state.queryParams.IsConnected) {
-		case '': // 全部
+	switch (state.queryParams.type) {
+		case '0': // 全部
 			columns.value = allColumns;
 			break;
-		case '1': // 呼入已接
+		case '1': // 呼入列表(已接通)
 			columns.value = inColumns;
-			requestParams.value.CallDirection = 0;
-			requestParams.value.OnState = 1;
+			requestParams.value.IsConnected = true; // 是否接通
+			requestParams.value.Direction = 0; // 来电
 			break;
-		case '2': // 呼出已接
+		case '2': // 呼出列表(已接通)
 			columns.value = outColumns;
-			requestParams.value.CallDirection = 1;
-			requestParams.value.OnState = 1;
+			requestParams.value.IsConnected = true; // 是否接通
+			requestParams.value.Direction = 1; // 呼出
 			break;
-		case '3': // 未接
+		case '3': // 未接列表
 			columns.value = noColumns;
-			requestParams.value.OnState = 2;
-			break;
-		case '4': // 智能应答
-			columns.value = smartColumns;
-			requestParams.value.IsAiAnswered = true;
-			requestParams.value.OnState = 1;
-			requestParams.value.CallDirection = 0;
-			break;
-		case '5': // 呼入白名单
-			columns.value = whiteColumns;
-			requestParams.value.PhoneTypes = 1; // 呼入VIP
-			requestParams.value.CallDirection = 0; // 来电
-			break;
-		case '6': // 呼入黑名单
-			columns.value = blackColumns;
-			requestParams.value.PhoneTypes = 2; // 呼入黑名单
+			requestParams.value.IsConnected = false; // 是否接通
 			break;
 		default:
 			columns.value = allColumns;

+ 277 - 0
src/views/tels/phoneAction.vue

@@ -0,0 +1,277 @@
+<template>
+	<div class="tels-callLog-container layout-pd">
+		<el-card shadow="never">
+			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent label-width="20px">
+				<el-row :gutter="10">
+					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
+						<el-form-item prop="UserName">
+							<el-input v-model="state.queryParams.UserName" placeholder="坐席人员" clearable @keyup.enter="handleQuery" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
+						<el-form-item prop="StaffNo">
+							<el-input v-model="state.queryParams.StaffNo" placeholder="工号" clearable @keyup.enter="handleQuery" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
+						<el-form-item prop="GroupId">
+							<el-input v-model="state.queryParams.GroupId" placeholder="工作组" clearable @keyup.enter="handleQuery" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
+						<el-form-item prop="OperateState">
+							<el-select v-model="state.queryParams.OperateState" placeholder="动作类型" clearable class="w100" @change="handleQuery">
+								<el-option v-for="item in state.callDirectionOption" :value="item.key" :key="item.key" :label="item.value" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<transition name="el-zoom-in-top">
+						<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6" v-show="!searchCol">
+							<el-form-item prop="crTime">
+								<el-date-picker
+									v-model="state.queryParams.crTime"
+									type="datetimerange"
+									unlink-panels
+									range-separator="至"
+									start-placeholder="开始时间"
+									end-placeholder="结束时间"
+									:shortcuts="shortcuts"
+									@change="handleQuery"
+									value-format="YYYY-MM-DD[T]HH:mm:ss"
+									:default-time="defaultTimeStartEnd"
+								/>
+							</el-form-item>
+						</el-col>
+					</transition>
+				</el-row>
+				<div class="w100 ml20">
+					<el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+					<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+						<SvgIcon name="ele-Refresh" class="mr5" />重置
+					</el-button>
+					<el-button link type="primary" @click="closeSearch" :loading="state.loading">
+						{{ searchCol ? '展开' : '收起' }}
+						<SvgIcon :class="{ 'is-reverse': searchCol }" name="ele-ArrowUp" class="mr5 arrow" size="18px" />
+					</el-button>
+				</div>
+			</el-form>
+		</el-card>
+		<el-card shadow="never">
+			<ProTable
+				ref="proTableRef"
+				:columns="columns"
+				:data="state.tableData"
+				@updateTable="queryList"
+				:loading="state.loading"
+				:key="Math.random()"
+				:pagination="false"
+			>
+			</ProTable>
+			<div class="flex-end mt20">
+				<!--					<el-button class="default-button" @click="onChangeTotal">展示总数</el-button>-->
+				<el-pagination
+					layout="prev, pager, next"
+					:total="totalSize"
+					@current-change="handleCurrentChange"
+					:page-size="currentSize"
+					:current-page="currentPage"
+					:pager-count="5"
+				/>
+			</div>
+		</el-card>
+		<!-- 播放录音 -->
+		<play-record ref="playRecordRef" />
+		<!-- 业务关联 -->
+		<connect-business ref="connectBusinessRef" @updateList="queryList" />
+	</div>
+</template>
+
+<script lang="tsx" setup name="telsPhoneAction">
+import { defineAsyncComponent, onMounted, reactive, ref, onActivated, onBeforeUnmount } from 'vue';
+import type { FormInstance } from 'element-plus';
+import { ElMessageBox } from 'element-plus';
+import { downloadFileByStream, getNeedArr } from '@/utils/tools';
+import { callBaseData, callLogPaged } from '@/api/tels/callLog';
+import { formatDate } from '@/utils/formatTime';
+import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
+import other from '@/utils/other';
+import { useRouter } from 'vue-router';
+import { fileDownload } from '@/api/public/file';
+import mittBus from '@/utils/mitt';
+import Other from '@/utils/other';
+import { getCallCenterCallRecord, getCallCenterCallRecordBaseData, getCallCenterOperateRecord } from '@/api/callCenter';
+
+// 引入组件
+const PlayRecord = defineAsyncComponent(() => import('@/views/tels/callLog/component/Play-record.vue')); // 播放录音
+const ConnectBusiness = defineAsyncComponent(() => import('@/views/tels/callLog/component/Connect-business.vue')); // 关联工单还是回访
+const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
+
+const proTableRef = ref<RefType>(); // 表格ref
+// 表格配置项
+const columns = ref<any[]>([
+	{ prop: 'fromNo', label: '坐席人员', width: 120 },
+	{ prop: 'toNo', label: '工号', width: 120 },
+	{ prop: 'order.no', label: '工作组', width: 150 },
+	{ prop: 'order.title', label: '动作类型', width: 200 },
+	{
+		prop: 'answeredTime',
+		label: '开始时间',
+		width: 160,
+		render: (scope) => <span>{formatDate(scope.row.answeredTime, 'YYYY-mm-dd HH:MM:SS')}</span>,
+	},
+	{ prop: 'overTime', label: '结束时间', width: 160, render: (scope) => <span>{formatDate(scope.row.overTime, 'YYYY-mm-dd HH:MM:SS')}</span> },
+	{ prop: 'hangupBy', label: '使用时间(秒)' },
+]);
+// 定义变量内容
+const state = reactive<any>({
+	queryParams: {
+		PageIndex: 1, // 当前页
+		PageSize: 10, // 每页条数
+		UserName: null, // 话务员名称
+		StaffNo: null, // 工号
+		GroupId: null, // 工作组
+		OperateState: null, // 操作类型
+		crTime: [], // 时间
+		CallStartTimeBT: null,
+		CallStartTimeLT: null,
+	},
+	tableData: [], // 列表数据
+	loading: false, // 加载
+	total: 0, // 总条数
+	callDirection: [],
+	onState: [],
+	endByOptions: [],
+});
+const ruleFormRef = ref<FormInstance>(); // 表单ref
+const searchCol = ref(true); // 展开/收起
+// 展开/收起
+const closeSearch = () => {
+	searchCol.value = !searchCol.value;
+};
+// 手动查询,将页码设置为1
+const handleQuery = () => {
+	state.queryParams.PageIndex = 1;
+	queryIndex.value = 0;
+	queryList(true);
+};
+// 改变页码
+const currentPage = ref(1); // 当前页
+const currentSize = ref(10); // 每页条数
+const totalSize = ref(0); // 数据条总数
+const queryIndex = ref(0); // 数据批次
+const totalTable = ref([]); // 数据总数
+const handleCurrentChange = (val: number) => {
+	currentPage.value = val;
+	// 判断当前页是否是数据的最后一页
+	if (val === Math.ceil(totalSize.value / currentSize.value)) {
+		queryIndex.value++;
+		queryList();
+	} else {
+		state.tableData = getNeedArr(totalTable.value, currentSize.value)[currentPage.value - 1]; //当前页的表格数据
+	}
+};
+/** 通话记录列表 */
+const requestParams = ref({});
+const queryList = async (isQuery: boolean = false) => {
+	state.loading = true;
+	requestParams.value = Other.deepClone(state.queryParams);
+	requestParams.value.CallStartTimeBT = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
+	requestParams.value.CallStartTimeLT = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
+	Reflect.deleteProperty(requestParams.value, 'crTime'); // 通话开始和结束时间段
+
+	requestParams.value.QueryIndex = queryIndex.value; // 数据批次
+	state.loading = true;
+	getCallCenterOperateRecord(requestParams.value)
+		.then((response: any) => {
+			if (isQuery) {
+				// 如果是查询
+				totalTable.value = []; // 先清空
+				totalTable.value = response?.result;
+				totalSize.value = totalTable.value.length;
+				state.tableData = getNeedArr(totalTable.value, currentSize.value)[currentPage.value - 1]; //当前页的表格数据
+			} else {
+				totalTable.value = totalTable.value.concat(response?.result);
+				totalSize.value = totalTable.value.length;
+				state.tableData = getNeedArr(totalTable.value, currentSize.value)[currentPage.value - 1]; //当前页的表格数据
+			}
+			state.loading = false;
+		})
+		.catch(() => {
+			state.loading = false;
+		});
+};
+/** 重置按钮操作 */
+const resetQuery = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	queryList();
+};
+// 播放录音
+const playRecordRef = ref<RefType>();
+const onPlaySoundRecording = (val: any) => {
+	playRecordRef.value.openDialog(import.meta.env.VITE_RECORD_PREFIX + val.recordingAbsolutePath, val.recordingFileName, val.recordingAbsolutePath);
+};
+// 下载录音
+const onDownload = (row: any) => {
+	ElMessageBox.confirm(`您确定要下载此录音吗?`, '提示', {
+		confirmButtonText: '确认',
+		cancelButtonText: '取消',
+		type: 'warning',
+		draggable: true,
+		cancelButtonClass: 'default-button',
+		autofocus: false,
+	})
+		.then(() => {
+			fileDownload({ path: import.meta.env.VITE_RECORD_DOWNLOAD_PREFIX + row.recordingAbsolutePath }).then((res: any) => {
+				downloadFileByStream(res, row.recordingFileName);
+			});
+		})
+		.catch(() => {});
+};
+// 失联工单
+const router = useRouter();
+const onCreate = (row: any) => {
+	router.push({
+		name: 'orderAccept',
+		query: {
+			createBy: 'tel',
+			fromTel: row.cpn,
+			callId: row.otherAccept,
+			transfer: row.gateway,
+			telArea: '',
+		},
+	});
+};
+// 基础信息
+const getBaseData = async () => {
+	try {
+		const { result } = await getCallCenterCallRecordBaseData();
+		state.callDirectionOption = result.direction;
+		state.endByOptions = result.endBy;
+	} catch (e) {
+		console.log(e);
+	}
+};
+onMounted(() => {
+	getBaseData();
+	queryList();
+});
+onActivated(() => {
+	mittBus.on('clearCachePage', () => {
+		//清除缓存
+		queryList();
+	});
+});
+onBeforeUnmount(() => {
+	mittBus.off('clearCachePage');
+});
+</script>
+<style lang="scss" scoped>
+.arrow {
+	transition: transform var(--el-transition-duration);
+	cursor: pointer;
+}
+.arrow.is-reverse {
+	transform: rotateZ(-180deg);
+}
+</style>