Browse Source

reactor:流程调整,呼叫中心对接

zhangchong 1 năm trước cách đây
mục cha
commit
ed56b6693e

+ 10 - 25
src/components/ProcessAudit/index.vue

@@ -627,7 +627,6 @@ const canReject = ref<boolean>(false); // 是否可以驳回
 
 const handleResult = (res: any) => {
 	state.nextStepOptions = res.result.steps; //处理人选择内容
-	state.ruleForm.backToCountersignEnd = res.result.backToCountersignEnd ?? false; // 是否回到会签结束节点
   canReject.value = res.result.canReject ?? false; // 是否可以驳回
 
 	// state.handleId = res.result.id; //流程ID
@@ -637,22 +636,16 @@ const handleResult = (res: any) => {
 		// 办理才有期满时间
 		state.ruleForm.expiredTime = res.result.expiredTime ?? null; // 期满时间
 	}
-	/*if (state.nextStepOptions.length === 1) {
-		// 下一节点是否只有一个 默认选中第一个
-		state.ruleForm.nextStepCode = state.nextStepOptions[0].code; // 下一节点
-		getNextStepOption(res.result.id, state.nextStepOptions[0].code); // 查询流程下一节点参数
-		isCollect.value = state.nextStepOptions[0].stepType === 3 && handelArr.includes(state.processType); // 是否是汇总节点(汇总需要填入其他参数)
-	} else {
-		state.ruleForm.nextStepCode = '';
-		isCollect.value = false;
-	}*/
+
 	if (state.nextStepOptions.length === 1) {
 		// 下一节点是否只有一个 默认选中第一个
 		state.ruleForm.nextStepCode = state.nextStepOptions[0].key; // 下一节点code
     state.ruleForm.nextStepName = state.nextStepOptions[0].value; // 下一节点name
 		selectNextStep(state.nextStepOptions[0].key); // 查询流程下一节点参数
 
-		isCollect.value = state.nextStepOptions[0].stepType === 3 && handelArr.includes(state.processType); // 是否是汇总节点(汇总需要填入其他参数)
+		isCollect.value = state.nextStepOptions[0].inputRealHandler  // 需要填入其他参数
+
+    state.ruleForm.backToCountersignEnd = state.nextStepOptions[0].backToCountersignEnd ?? false; // 是否回到会签结束节点
 	} else {
 		state.ruleForm.nextStepCode = '';
     state.ruleForm.nextStepName = '';
@@ -791,22 +784,14 @@ const isCollect = ref(false); // 是否是汇总节点(汇总需要填入其
 const selectNextStep = (val: any) => {
 	ruleFormRef.value?.resetFields('nextHandlers');
 	ruleFormRef.value?.resetFields('nextMainHandler');
-	const items = state.nextStepOptions.find((item: any) => item.key === val).items;
-  state.ruleForm.nextStepName = state.nextStepOptions.find((item: any) => item.key === val).value; // 下一节点name
+  const next = state.nextStepOptions.find((item: any) => item.key === val);
+	const items = next.items; //获取下一节点
+  state.ruleForm.nextStepName = next.value; // 下一节点name
+  state.ruleForm.backToCountersignEnd = next.backToCountersignEnd ?? false; // 是否回到会签结束节点
 	state.handlerOptions = items ?? [];
-	/*const next = state.nextStepOptions.find((item: any) => item.code === val);
-	getNextStepOption(state.handleId, next.code);
-	isCollect.value = next.stepType === 3 && handelArr.includes(state.processType); // 是否是汇总节点(汇总需要填入其他参数)*/
+  isCollect.value = next.inputRealHandler; // 是否需要填入其他参数
 };
-// 查询流程下一节点参数
-/*const getNextStepOption = async (DefineId: string, Code: string) => {
-	try {
-		const res: any = await workflowStepOptions({ DefineId, Code });
-		state.handlerOptions = res.result.handlers ?? [];
-	} catch (error) {
-		console.log(error);
-	}
-};*/
+
 // 选择处理人
 const selectHandlers = () => {
 	ruleFormRef.value?.resetFields('nextMainHandler');

+ 8 - 0
src/layout/navBars/breadcrumb/index.vue

@@ -17,6 +17,8 @@ import { useRoutesList } from '/@/stores/routesList';
 import { useThemeConfig } from '/@/stores/themeConfig';
 import { useUserInfo } from '/@/stores/userInfo';
 import mittBus from '/@/utils/mitt';
+import signalR from '/@/utils/signalR';
+
 // 定义接口来定义对象的类型
 interface IndexState {
 	menuList: object[];
@@ -38,6 +40,10 @@ const route = useRoute();
 const state = reactive<IndexState>({
 	menuList: [],
 });
+
+//  signalR 初始化signalr
+signalR.init();
+
 // 获取用户信息配置
 const userInfosConfig = computed(() => {
 	return userInfos.value;
@@ -100,6 +106,8 @@ onMounted(() => {
 	mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
 		setFilterRoutes();
 	});
+  // 加入分组
+  signalR.joinGroup('CallCenter');
 });
 // 页面卸载时
 onUnmounted(() => {

+ 228 - 195
src/layout/navBars/breadcrumb/telControl.vue

@@ -78,7 +78,9 @@
 					@mouseleave="onHover('restSrc', 'phoneControls/rest_blue.png')"
 				>
 					<img :src="state.restSrc" alt="" />
-					<span>结束<span v-if="restReason">({{restReason}})</span></span>
+					<span
+						>结束<span v-if="restReason">({{ restReason }})</span></span
+					>
 				</div>
 				<div
 					class="item active"
@@ -246,7 +248,7 @@
 					<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
 						<el-form-item label="小休" prop="reason" :rules="[{ required: true, message: '请选择小休原因', trigger: 'change' }]">
 							<el-select v-model="state.restForm.reason" placeholder="请选择小休原因" class="w100" clearable>
-                <el-option v-for="item in state.restReasonOptions" :key="item.dicDataValue" :label="item.dicDataName" :value="item.dicDataValue" />
+								<el-option v-for="item in state.restReasonOptions" :key="item.dicDataValue" :label="item.dicDataName" :value="item.dicDataValue" />
 							</el-select>
 						</el-form-item>
 					</el-col>
@@ -325,7 +327,7 @@
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 						<el-form-item label="小休" prop="reason" :rules="[{ required: true, message: '请选择小休原因', trigger: 'change' }]">
 							<el-select v-model="state.restForm.reason" placeholder="请选择小休原因" class="w100" clearable>
-              <el-option v-for="item in state.restReasonOptions" :key="item.dicDataValue" :label="item.dicDataName" :value="item.dicDataValue" />
+								<el-option v-for="item in state.restReasonOptions" :key="item.dicDataValue" :label="item.dicDataName" :value="item.dicDataValue" />
 							</el-select>
 						</el-form-item>
 					</el-col>
@@ -386,22 +388,24 @@
 			</span>
 		</template>
 	</el-dialog>
+
+  <!-- 三方通话弹窗 -->
+
 </template>
 
 <script setup lang="ts" name="telControl">
-import { reactive, ref, computed, defineAsyncComponent, onMounted, onBeforeMount } from 'vue';
+import { reactive, ref, computed, defineAsyncComponent, onMounted } from 'vue';
 import { ElMessageBox, ElNotification, ElMessage, FormInstance } from 'element-plus';
 import { storeToRefs } from 'pinia';
 import { useTelStatus, TelStates, RestStates } from '/@/stores/telStatus';
 import { useUserInfo } from '/@/stores/userInfo';
 import { useAppConfig } from '/@/stores/appConfig';
-import { debounce, getImageUrl } from '/@/utils/tools';
+import { getImageUrl } from '/@/utils/tools';
 import { formatDuration } from '/@/utils/formatTime';
 import { commonEnum } from '/@/utils/constants';
 import other from '/@/utils/other';
-import mittBus from '/@/utils/mitt';
 import { workflowStepOptions } from '/@/api/system/workflow';
-import {restFlowStart, restFlowDel, restFlowStartWex, getTelList, telRestProcess, telRestAdd, telRestBaseData} from '/@/api/public/wex';
+import { restFlowStart, restFlowDel, restFlowStartWex, getTelList, telRestProcess, telRestAdd, telRestBaseData } from '/@/api/public/wex';
 import { auth } from '/@/utils/authFunction';
 import { VoiceInterfaceObject } from '/@/utils/PhoneScript';
 import signalR from '/@/utils/signalR';
@@ -478,37 +482,33 @@ const { AppConfigInfo } = storeToRefs(appConfigStore); // 系统配置信息
 const storesUserInfo = useUserInfo();
 const { userInfos } = storeToRefs(storesUserInfo); // 用户信息
 
-const talkTime = ref<Number>(0);// 通话时长
-const talkTimer = ref<any>(null);// 通话时长定时器
+const talkTime = ref<any>(0); // 通话时长
+const talkTimer = ref<any>(null); // 通话时长定时器
+
 // 开始计时
-const startTime = debounce(() => {
-	let talkTime = Local.get('talkTime');
+const startTime = () => {
+	let localTalkTime = Local.get('talkTime');
 	if (talkTime) {
-    talkTime.value = Number(talkTime);
-    talkTimer.value = setInterval(() => {
-      talkTime.value++;
+		talkTime.value = Number(localTalkTime);
+		talkTimer.value = setInterval(() => {
+			talkTime.value++;
 			Local.set('talkTime', String(talkTime.value));
 		}, 1000);
 	} else {
-    talkTimer.value = setInterval(() => {
-      talkTime.value++;
+		talkTimer.value = setInterval(() => {
+			talkTime.value++;
 			Local.set('talkTime', String(talkTime.value));
 		}, 1000);
 	}
-}, 1000);
+};
 // 结束计时
-const removeTimer = debounce(() => {
-  talkTime.value = 0;
+const removeTimer = () => {
+	talkTime.value = 0;
 	Local.remove('talkTime');
 	clearInterval(talkTimer.value);
-}, 1000);
-
-//  signalR 初始化signalr
-signalR.init();
+};
 // 监听消息
 const signalRStart = () => {
-	// 加入分组
-	signalR.joinGroup('CallCenter');
 	signalR.SR.on('Send', (data: any) => {
 		// 加入分组成功
 		console.log(data, '加入分组成功');
@@ -527,7 +527,7 @@ const activeArr = computed(() => {
 		dutyOn: ['dutyOff', 'rest', 'outbound', 'callForwarding'], // 已签入无通话状态
 		rest: ['rest', 'callForwarding'], // 小休中状态
 		ring: ['hangup'], //振铃中
-		onCall: ['hangup', 'hold', 'transfer', 'evaluate'], // 单个通话中
+		onCall: ['hangup', 'hold', 'transfer', 'evaluate','conference'], // 单个通话中
 		onHold: ['hangup', 'hold', 'transfer', 'evaluate'], // 保持中
 		onTalkingDeal: ['dutyOff', 'rest', 'outbound', 'callForwarding', 'TalkingDeal'], // 事后处理中
 		onTransferSuccess: ['hangup', 'conference'], // 转接成功
@@ -556,12 +556,12 @@ const currentStatusText = computed(() => {
 });
 // 小休审批通过消息
 const RestApplyPassFn = (data: any) => {
-  ElNotification({
-    title: '成功',
-    message: '小休审批通过,开始小休',
-    type: 'success',
-  });
-  VoiceInterfaceObject.SetBusy(data); //设置忙碌
+	ElNotification({
+		title: '成功',
+		message: '小休审批通过,开始小休',
+		type: 'success',
+	});
+	VoiceInterfaceObject.SetBusy(data); //设置忙碌
 };
 // 查询所有分机
 const getTelsLists = async (object?: object) => {
@@ -643,71 +643,75 @@ const onControlClick = (val: string) => {
 
 // 当前分机对象
 const ola_object = {
-  ola_extn: '1001', //分机hao
-  ola_password: '!@#123Qw', //分机密码
-  queues: '10010', //队列
+	ola_extn: '1001', //分机hao
+	ola_password: '!@#123Qw', //分机密码
+	queues: '10010', //队列
 };
 // 链接呼叫中心
 const websocket_connect = () => {
-  ola.onConnect = onConnect;
-  ola.onClose = onClose;
-  ola.onMessage = onMessage;
-  ola.connect(import.meta.env.VITE_CALLCENTER_SOCKET_URL, ola_object.ola_extn, ola_object.ola_password,);
+	ola.onConnect = onConnect;
+	ola.onClose = onClose;
+	ola.onMessage = onMessage;
+	ola.connect(import.meta.env.VITE_CALLCENTER_SOCKET_URL, ola_object.ola_extn, ola_object.ola_password);
 };
 // 呼叫中心链接
 const onConnect = () => {
-  ola.subscribe('ola.agent.' + ola_object.ola_extn);
-  ola.subscribe('ola.caller.' + ola_object.ola_extn);
-  ola.get_agent_state(ola_object.ola_extn);
+	ola.subscribe('ola.agent.' + ola_object.ola_extn);
+	ola.subscribe('ola.caller.' + ola_object.ola_extn);
+	ola.get_agent_state(ola_object.ola_extn);
 
-  callCenterLogin();
+	callCenterLogin();
 };
 // 呼叫中心登录
 const callCenterLogin = () => {
-  ola.logout(ola_object.ola_extn); //连接之后,先登出一次,防止其他地方已经登陆
-  let array_ola_queue: EmptyArrayType = []; // 队列
-  if (ola_object.queues) {
-    let array = ola_object.queues.split(',');
-    for (let i = 0; i < array.length; i++) {
-      array_ola_queue[i] = array[i];
-    }
-    ola.login(array_ola_queue, ola_object.ola_extn, { type: 'onhook' });
-  }
+	ola.logout(ola_object.ola_extn); //连接之后,先登出一次,防止其他地方已经登陆
+	let array_ola_queue: EmptyArrayType = []; // 队列
+	if (ola_object.queues) {
+		let array = ola_object.queues.split(',');
+		for (let i = 0; i < array.length; i++) {
+			array_ola_queue[i] = array[i];
+		}
+		ola.login(array_ola_queue, ola_object.ola_extn, { type: 'onhook' });
+	}
 };
 // 呼叫中心消息
 const onMessage = (event: any) => {
-  const data = JSON.parse(event.data);
-  console.log('onMessage', data);
-  if (data.event_type == 'agent_state') {
-    // 坐席状态
-    if (data.state == 'login') {
-      console.log('已签入');
-      // 设置分机号和坐席组
-      useTelStatusStore.setCallInfo({ telsNo: ola_object.ola_extn });
-      // 设置签入状态
-      useTelStatusStore.setDutyState(true);
-      // 设置电话状态
-      useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
-      setTimeout(() => {
-        // 设置示闲状态
-       ola.go_ready();
-      }, 1000);
-    } else if (data.state == 'logout') {
-      console.log('未签入');
-      state.loading = true;
-      // 重置所有状态
-      useTelStatusStore.resetState();
-      state.loading = false;
-    } else if (data.state == 'ready') {
-      // 设置休息状态 设置未正常状态
-      useTelStatusStore.setRest(RestStates.unRest);
-      // 设置话机状态 结束休息改为签入状态
-      useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
-      console.log('示闲中');
-    } else if (data.state == 'unready') {
-      useTelStatusStore.setPhoneControlState(TelStates.rest);
-      useTelStatusStore.setRest(RestStates.resting);
-      /*if (AppConfigInfo.value.IsRestApproval) {
+	const data = JSON.parse(event.data);
+	console.log('onMessage', data);
+	if (data.event_type == 'agent_state') {
+		// 坐席状态
+		if (data.state == 'login') {
+			// 签入
+			// 设置分机号和坐席组
+			useTelStatusStore.setCallInfo({ telsNo: ola_object.ola_extn });
+			// 设置签入状态
+			useTelStatusStore.setDutyState(true);
+			// 设置电话状态
+			useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
+			setTimeout(() => {
+				// 设置示闲状态
+				ola.go_ready();
+			}, 1000);
+			console.log('已签入');
+		} else if (data.state == 'logout') {
+			// 签出
+			state.loading = true;
+			// 重置所有状态
+			useTelStatusStore.resetState();
+			state.loading = false;
+			console.log('已签出');
+		} else if (data.state == 'ready') {
+			// 示闲中
+			// 设置休息状态 设置未正常状态
+			useTelStatusStore.setRest(RestStates.unRest);
+			// 设置话机状态 结束休息改为签入状态
+			useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
+			console.log('示闲中');
+		} else if (data.state == 'unready') {
+			// 示忙中
+			useTelStatusStore.setPhoneControlState(TelStates.rest);
+			useTelStatusStore.setRest(RestStates.resting);
+			/*if (AppConfigInfo.value.IsRestApproval) {
         // 如果小休需要审核
         telRestProcess()
             .then((res: any) => {
@@ -740,105 +744,149 @@ const onMessage = (event: any) => {
 
             });
       }*/
-      console.log('示忙中');
-      break_reason(data.private_data);
-    } else if (data.state == 'acw') {
-      console.log('话后整理中');
-      const time: number = AppConfigInfo.value.TalkingDealTime * 1000; // 事后处理时间
+			break_reason(data.private_data);
+			console.log('示忙中');
+		} else if (data.state == 'acw') {
+			// 话后整理中
+			const time: number = AppConfigInfo.value.TalkingDealTime * 1000; // 事后处理时间
+			ElNotification({
+				title: '自动开启事后处理成功',
+				message: `${AppConfigInfo.value.TalkingDealTime}秒后自动结束事后处理,或者手动结束事后处理`,
+				type: 'success',
+				duration: time,
+			});
+			// 设置事后处理
+			useTelStatusStore.setTalkingDeal(true);
+			// 设置话机状态 设置为事后处理中
+			useTelStatusStore.setPhoneControlState(TelStates.onTalkingDeal);
+			setTimeout(() => {
+				// 设置事后处理
+				useTelStatusStore.setTalkingDeal(false);
+				// 设置话机状态 取消事后处理修改为空闲状态
+				useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
+				ola.go_ready(); // 示闲
+			}, time);
+			console.log('话后整理中');
+		} else if (data.state == 'busy') {
 
-      ElNotification({
-        title: '自动开启事后处理成功',
-        message: `${AppConfigInfo.value.TalkingDealTime}秒后自动结束事后处理,或者手动结束事后处理`,
-        type: 'success',
-        duration: time,
-      });
-      // 设置事后处理
-      useTelStatusStore.setTalkingDeal(true);
-      // 设置话机状态 设置为事后处理中
-      useTelStatusStore.setPhoneControlState(TelStates.onTalkingDeal);
-      setTimeout(() => {
-        // 设置事后处理
-        useTelStatusStore.setTalkingDeal(false);
-        // 设置话机状态 取消事后处理修改为空闲状态
-        useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
-        ola.go_ready(); // 示闲
-      }, 10000);
+		} else {
+			console.log(data.state, '其他状态');
+		}
 
-    } else if (data.state == 'busy') {
-      console.log('通话中');
-    } else {
-      console.log(data.state);
-    }
+		if (data.state == 'busy') {
+      holdStatus(data.private_data); //处理保持
+			if (data.call_direction == 'outbound') {
+				// 呼出
+				if (data.private_data == 'calling') {
+					// 拨号中
+					// 设置电话状态 振铃中
+					useTelStatusStore.setPhoneControlState(TelStates.ring);
+					console.log('呼出拨号中');
+				} else if (data.private_data == 'answered') {
+					//振铃中
+					if (data.other_answered == false) {
+						// 设置电话状态 振铃中
+						useTelStatusStore.setPhoneControlState(TelStates.ring);
+						console.log('呼出振铃中');
+					} else if (data.other_answered == true) {
+						// 通话中
+            // 开始计时
+            startTime();
+						// 设置电话状态 通话中
+						useTelStatusStore.setPhoneControlState(TelStates.onCall);
+						console.log('呼出通话中');
+					}
+				}
+			} else {
+				// 呼入
+				if (data.private_data == 'ring') {
+					// 设置电话状态 振铃中
+					useTelStatusStore.setPhoneControlState(TelStates.ring);
+					console.log('呼入振铃中');
+				} else if (data.private_data == 'answered') {
+          // 开始计时
+          startTime();
+					// 设置电话状态 通话中
+					useTelStatusStore.setPhoneControlState(TelStates.onCall);
+					console.log('呼入通话中');
+				}
+			}
+		} else if (data.old_state == 'busy') {
+			//挂机后系统可以返回两种状态:acw 话后整理状态 ready 示闲状态,如果不需要acw,可以联系我们后台修改配置,如果需要保留,如果需要再次
+			//拨打电话的话,需要手动点击示闲按钮
 
-    if (data.state == 'busy') {
-      if (data.call_direction == 'outbound') {
-        if (data.private_data == 'calling') {
-          console.log('拨号中');
-        } else if (data.private_data == 'answered') {
-          if (data.other_answered == false) {
-            console.log('振铃中');
-          } else if (data.other_answered == true) {
-            console.log('通话中');
-          }
-        }
-      } else {
-        if (data.private_data == 'ring') {
-          console.log('振铃中');
-        } else if (data.private_data == 'answered') {
-          console.log('通话中');
-        }
-      }
-    } else if (data.old_state == 'busy') {
-      //挂机后系统可以返回两种状态:acw 话后整理状态 ready 示闲状态,如果不需要acw,可以联系我们后台修改配置,如果需要保留,如果需要再次
-      //拨打电话的话,需要手动点击示闲按钮
-      alert('已挂机');
-    }
-  } else if (data.event_type == 'agent_caller_state') {
-    //通话状态
-    // special feature, never mind
-    if (data.action == 'in') {
-      console.log('呼入', data.caller.cid_number, data.caller.uuid);
-      // ola.take_call(data.caller.uuid);
-    } else {
-      console.log('呼出', data.caller.uuid);
-    }
-  } else if (data.event_type == 'command/reply') {
-    // 其他消息
-    // console.log('command/reply', data);
-  }
+			// 设置电话状态
+			useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
+			// 结束计时
+			removeTimer();
+			console.log('已挂机');
+		}
+	} else if (data.event_type == 'agent_caller_state') {
+		//通话状态
+		// special feature, never mind
+		if (data.action == 'in') {
+			console.log('呼入', data.caller.cid_number, data.caller.uuid);
+			// ola.take_call(data.caller.uuid);
+		} else {
+			console.log('呼出', data.caller.uuid);
+		}
+	} else if (data.event_type == 'command/reply') {
+		// 其他消息
+		// console.log('command/reply', data);
+	}
 };
 // 呼叫中心链接关闭
 const onClose = () => {
-  ElMessage.error('呼叫中心断开链接');
+	ElMessage.error('呼叫中心断开链接');
 };
 // 小休原因
 const restReason = ref(''); // 小休原因
 const break_reason = (reason: string) => {
-  switch (reason) {
-    case 'away':
-      restReason.value = '外出中';
-      break;
-    case 'rest':
-      restReason.value = '休息中';
-      break;
-    case 'conference':
-      restReason.value = '会议中';
-      break;
-    case 'train':
-      restReason.value = '培训中';
+	switch (reason) {
+		case 'away':
+			restReason.value = '外出中';
+			break;
+		case 'rest':
+			restReason.value = '休息中';
+			break;
+		case 'conference':
+			restReason.value = '会议中';
+			break;
+		case 'train':
+			restReason.value = '培训中';
+			break;
+		case 'eat':
+			restReason.value = '用餐中';
+			break;
+		default:
+			restReason.value = '休息中';
+			break;
+	}
+};
+// 保持状态处理
+const holdStatus = (holdStatus:string)=>{
+  switch (holdStatus) {
+    case 'held':
+      // 设置电话状态
+      useTelStatusStore.setHold(true);
+      // 设置电话状态 保持中
+      useTelStatusStore.setPhoneControlState(TelStates.onHold);
       break;
-    case 'eat':
-      restReason.value = '用餐中';
+    case 'unheld':
+      // 设置电话状态 通话中
+      // 设置电话状态  取消单个保持为通话中
+      useTelStatusStore.setHold(false);
+      // 设置电话状态
+      useTelStatusStore.setPhoneControlState(TelStates.onCall);
       break;
     default:
-      restReason.value = '休息中';
       break;
   }
-};
+}
 //签入
 const dutyFormRef = ref<RefType>();
 const onDutyFn = async () => {
-  websocket_connect();
+	websocket_connect();
 	/*dutyFormRef.value?.resetFields();
 	// 获取所有分机列表(未签入的)
 	const list = await getTelsLists({ sigin: 0 });
@@ -856,7 +904,7 @@ const clickOnDuty = (formEl: FormInstance | undefined) => {
 		if (!valid) return;
 		state.loading = true;
 		// {telNo:state.dutyForm.telNo}
-    websocket_connect(); //开启消息监听
+		websocket_connect(); //开启消息监听
 	});
 };
 // 签出
@@ -875,7 +923,7 @@ const offDutyFn = () => {
 			// 重置所有状态
 			useTelStatusStore.resetState();
 			state.loading = false;
-      state.dutyOnSrc =  getImageUrl('phoneControls/dutyOn_blue.png'); //签入图片
+			state.dutyOnSrc = getImageUrl('phoneControls/dutyOn_blue.png'); //签入图片
 		})
 		.catch(() => {
 			state.loading = false;
@@ -893,8 +941,8 @@ const onHangup = () => {
 	})
 		.then(() => {
 			state.loading = true;
-      ola.hangup(); //挂断
-      state.loading = false;
+			ola.hangup(); //挂断
+			state.loading = false;
 		})
 		.catch(() => {});
 };
@@ -1010,7 +1058,7 @@ const onRestEnd = () => {
 	})
 		.then(() => {
 			state.loading = true;
-      ola.go_ready(); // 示闲
+			ola.go_ready(); // 示闲
 			state.loading = false;
 		})
 		.catch(() => {});
@@ -1027,7 +1075,7 @@ const onHold = () => {
 	})
 		.then(() => {
 			state.loading = true;
-      ola.hold(); //保持
+			ola.hold(); //保持
 			state.loading = false;
 		})
 		.catch(() => {});
@@ -1044,15 +1092,13 @@ const onUnHold = () => {
 	})
 		.then(() => {
 			state.loading = true;
-      ola.hold(); //保持
+			ola.unhold(); //取消保持
 			state.loading = false;
 		})
 		.catch(() => {});
 };
 // 事后处理(系统默认进入)
-const onTalkingDeal = () => {
-
-};
+const onTalkingDeal = () => {};
 // 取消事后处理
 const unTalkingDeal = () => {
 	ElMessageBox.confirm(`确定要取消事后处理,是否继续?`, '提示', {
@@ -1065,11 +1111,11 @@ const unTalkingDeal = () => {
 	})
 		.then(() => {
 			state.loading = true;
-      // 设置事后处理
-      useTelStatusStore.setTalkingDeal(false);
-      // 设置话机状态 取消事后处理修改为空闲状态
-      useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
-      ola.go_ready(); // 示闲
+			// 设置事后处理
+			useTelStatusStore.setTalkingDeal(false);
+			// 设置话机状态 取消事后处理修改为空闲状态
+			useTelStatusStore.setPhoneControlState(TelStates.dutyOn);
+			ola.go_ready(); // 示闲
 			state.loading = false;
 		})
 		.catch(() => {});
@@ -1089,7 +1135,7 @@ const clickOnTransfer = (formEl: FormInstance | undefined) => {
 	if (!formEl) return;
 	formEl.validate((valid: boolean) => {
 		if (!valid) return;
-    ola.transfer(state.transferForm.telNo); //转接
+		ola.transfer(state.transferForm.telNo); //转接
 		state.transferDialogVisible = false;
 	});
 };
@@ -1135,21 +1181,8 @@ onMounted(() => {
 	signalRStart(); //开启消息监听
 	if (telStatusInfo.value.telsNo) {
 		// 有分机号
-    websocket_connect(); // 链接呼叫中心
+		websocket_connect(); // 链接呼叫中心
 	}
-	mittBus.on('startTalkTime', (message: string) => {
-		// 开始计时
-		startTime();
-	});
-	mittBus.on('endTalkTime', () => {
-		// 结束计时
-		removeTimer();
-	});
-});
-onBeforeMount(() => {
-	mittBus.off('startTalkTime'); // 移除监听
-	mittBus.off('endTalkTime'); // 移除监听
-	signalR.leaveGroup('CallCenter'); // 离开分组
 });
 </script>
 

+ 223 - 193
src/layout/navBars/breadcrumb/user.vue

@@ -1,7 +1,7 @@
 <template>
 	<!--  :style="{ flex: layoutUserFlexNum }" -->
 	<div class="layout-navbars-breadcrumb-user pr15">
-<!--		 <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
+		<!--		 <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
 			<div class="layout-navbars-breadcrumb-user-icon mr10"  title="组件大小">
 				<i class="iconfont icon-ziti"></i>
 			</div>
@@ -13,59 +13,68 @@
 				</el-dropdown-menu>
 			</template>
 		</el-dropdown>-->
-		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onSearchClick"  title="菜单搜索">
-      <SvgIcon name="ele-Search" size="16px"/>
+		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onSearchClick" title="菜单搜索">
+			<SvgIcon name="ele-Search" size="16px" />
 		</div>
-    <Search ref="searchRef" />
-		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onLayoutSetingClick" title="布局配置"  v-if="showSetting">
-      <SvgIcon name="icon-skin iconfont" size="18px"/>
+		<Search ref="searchRef" />
+		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onLayoutSetingClick" title="布局配置" v-if="showSetting">
+			<SvgIcon name="icon-skin iconfont" size="18px" />
 		</div>
-		 <div class="layout-navbars-breadcrumb-user-icon mr10" title="消息通知">
-       <UserNews />
+		<div class="layout-navbars-breadcrumb-user-icon mr10" title="消息通知">
+			<div class="layout-navbars-breadcrumb-user-icon">
+				<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false" v-model:visible="newsVisible">
+					<template #reference>
+						<el-badge :value="messageCount" :max="99" :hidden="messageHidden">
+							<SvgIcon name="ele-Bell" size="18px" />
+						</el-badge>
+					</template>
+					<template #default>
+						<user-news @hideNws="hideNws" />
+					</template>
+				</el-popover>
+			</div>
 		</div>
-		 <div class="layout-navbars-breadcrumb-user-icon mr10" @click="onLockScreen" title="锁屏">
-       <SvgIcon name="ele-Lock" size="18px"/>
+		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onLockScreen" title="锁屏">
+			<SvgIcon name="ele-Lock" size="18px" />
 		</div>
-<!--		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick" :title="isScreenfull ? '关全屏' : '开全屏'" >
+		<!--		<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick" :title="isScreenfull ? '关全屏' : '开全屏'" >
 			<i class="iconfont" :class="!isScreenfull ? 'icon-quanping1' : 'icon-tuichuquanping'"></i>
 		</div>-->
-    <div class="layout-navbars-breadcrumb-user-link" :title="userInfos.name">
-        <el-popover
-            ref="popover"
-            placement="bottom"
-            :width="250"
-            trigger="click"
-            popper-class="user-info-box"
-        >
-          <template #reference>
-            <div class="layout-navbars-breadcrumb-user-link-photoBox">
-              <el-avatar :src="userInfos.photo" :size="24" class="layout-navbars-breadcrumb-user-link-photo">
-                <SvgIcon name="ele-UserFilled" color="var(--el-color-primary)" />
-              </el-avatar>
-              <span class="text-no-wrap layout-navbars-breadcrumb-user-link-photoBox-name">{{ userInfos.name ? userInfos.name : '' }}</span>
-              <el-button link><SvgIcon name="ele-ArrowDown"/></el-button>
-            </div>
-          </template>
-          <div class="user-info-box-content">
-            <div class="user-info-box-content-item">
-              <span class="user-info-box-content-item-title">登录账号:</span><span class="user-info-box-content-item-content">{{ userInfos.account?.userName ?? '' }}</span>
-            </div>
-            <div class="mt5 user-info-box-content-item">
-              <span class="user-info-box-content-item-title">工号:</span><span class="user-info-box-content-item-content">{{ userInfos.staffNo }}</span>
-            </div>
-            <div class="mt5 user-info-box-content-item">
-              <span class="user-info-box-content-item-title">职务:</span><span class="user-info-box-content-item-content">{{ userInfos.roles.map(item=>item.displayName).join(',') }}</span>
-            </div>
-            <div class="mt5 user-info-box-content-item">
-              <span class="user-info-box-content-item-title">组织:</span><span class="user-info-box-content-item-content">{{ userInfos.orgName }}</span>
-            </div>
-            <div class="mt5 flex-between">
-              <el-button link type="primary" @click="onChangePwd">修改密码</el-button>
-              <el-button link type="primary" @click="onLogOut">退出登录</el-button>
-            </div>
-          </div>
-        </el-popover>
-    </div>
+		<div class="layout-navbars-breadcrumb-user-link" :title="userInfos.name">
+			<el-popover ref="popover" placement="bottom" :width="250" trigger="click" popper-class="user-info-box" v-model:visible="userVisible">
+				<template #reference>
+					<div class="layout-navbars-breadcrumb-user-link-photoBox" @userVisible="userVisible = !userVisible">
+						<el-avatar :src="userInfos.photo" :size="24" class="layout-navbars-breadcrumb-user-link-photo">
+							<SvgIcon name="ele-UserFilled" color="var(--el-color-primary)" />
+						</el-avatar>
+						<span class="text-no-wrap layout-navbars-breadcrumb-user-link-photoBox-name">{{ userInfos.name ? userInfos.name : '' }}</span>
+						<el-button link><SvgIcon name="ele-ArrowUp" class="mr5 arrow" :class="{ 'is-reverse': userVisible }" /></el-button>
+					</div>
+				</template>
+				<div class="user-info-box-content">
+					<div class="user-info-box-content-item">
+						<span class="user-info-box-content-item-title">登录账号:</span
+						><span class="user-info-box-content-item-content">{{ userInfos.account?.userName ?? '' }}</span>
+					</div>
+					<div class="mt5 user-info-box-content-item">
+						<span class="user-info-box-content-item-title">工号:</span
+						><span class="user-info-box-content-item-content">{{ userInfos.staffNo }}</span>
+					</div>
+					<div class="mt5 user-info-box-content-item">
+						<span class="user-info-box-content-item-title">职务:</span
+						><span class="user-info-box-content-item-content">{{ userInfos.roles.map((item) => item.displayName).join(',') }}</span>
+					</div>
+					<div class="mt5 user-info-box-content-item">
+						<span class="user-info-box-content-item-title">组织:</span
+						><span class="user-info-box-content-item-content">{{ userInfos.orgName }}</span>
+					</div>
+					<div class="mt5 flex-between">
+						<el-button link type="primary" @click="onChangePwd">修改密码</el-button>
+						<el-button link type="primary" @click="onLogOut">退出登录</el-button>
+					</div>
+				</div>
+			</el-popover>
+		</div>
 
 		<!-- 锁频 -->
 		<el-dialog v-model="dialogVisible" width="400px" draggable title="锁屏">
@@ -73,7 +82,7 @@
 				<el-form-item label="锁屏密码" prop="pwd" :rules="[{ required: true, message: '请输入锁屏密码', trigger: 'blur' }]">
 					<el-input show-password placeholder="请输入密码" v-model="state.ruleForm.pwd" @keyup.enter="confirmLock(ruleFormRef)">
 						<template #prefix>
-              <SvgIcon name="ele-Unlock" />
+							<SvgIcon name="ele-Unlock" />
 						</template>
 					</el-input>
 				</el-form-item>
@@ -90,30 +99,16 @@
 		<el-dialog v-model="showDutyDialog" draggable title="修改密码" width="500px">
 			<el-form :model="state.dutyForm" label-width="80px" :rules="rules" ref="dutyFormRef">
 				<el-form-item prop="currentPassword" label="旧密码" class="mb30">
-					<el-input
-						class="inputDeep"
-						clearable
-						show-password
-						placeholder="请输旧就密码"
-						v-model="state.dutyForm.currentPassword"
-						autocomplete="off"
-					>
+					<el-input class="inputDeep" clearable show-password placeholder="请输旧就密码" v-model="state.dutyForm.currentPassword" autocomplete="off">
 						<template #prefix>
-              <SvgIcon name="ele-Unlock" />
+							<SvgIcon name="ele-Unlock" />
 						</template>
 					</el-input>
 				</el-form-item>
 				<el-form-item label="新密码" prop="newPassword" class="mb30">
-					<el-input
-						class="inputDeep"
-						clearable
-						show-password
-						placeholder="请输入新密码"
-						v-model="state.dutyForm.newPassword"
-						autocomplete="off"
-					>
+					<el-input class="inputDeep" clearable show-password placeholder="请输入新密码" v-model="state.dutyForm.newPassword" autocomplete="off">
 						<template #prefix>
-              <SvgIcon name="ele-Unlock" />
+							<SvgIcon name="ele-Unlock" />
 						</template>
 					</el-input>
 					<div class="intensity">
@@ -134,7 +129,7 @@
 						autocomplete="off"
 					>
 						<template #prefix>
-              <SvgIcon name="ele-Unlock" />
+							<SvgIcon name="ele-Unlock" />
 						</template>
 					</el-input>
 				</el-form-item>
@@ -158,10 +153,12 @@ import { storeToRefs } from 'pinia';
 import { useUserInfo } from '/@/stores/userInfo';
 import { useTelStatus } from '/@/stores/telStatus';
 import { useThemeConfig } from '/@/stores/themeConfig';
-import {Session, Local, Cookie} from '/@/utils/storage';
+import { Session, Local, Cookie } from '/@/utils/storage';
 import mittBus from '/@/utils/mitt';
 import { VoiceInterfaceObject } from '/@/utils/PhoneScript';
 import { changePwd } from '/@/api/login/user';
+import { megcount, muquery } from '/@/api/auxiliary/notice';
+import signalR from '/@/utils/signalR';
 
 // 引入组件
 const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
@@ -272,102 +269,123 @@ const onScreenfullClick = () => {
 const onLayoutSetingClick = () => {
 	mittBus.emit('openSetingsDrawer');
 };
+const userVisible = ref<boolean>(false); //是否显示用户信息
+const newsVisible = ref<boolean>(false); //是否显示消息
+const messageCount = ref<number>(0); //消息数量
+const messageHidden = computed(() => {
+	//消息是否隐藏
+	return messageCount.value === 0;
+});
+// 获取数量
+const getNumAndList = async () => {
+	try {
+		const [megcountRes] = await Promise.all([megcount()]);
+		messageCount.value = megcountRes.result?.personCount + megcountRes.result?.orgCount;
+	} catch (e) {
+		console.log(e, '获取消息数量失败');
+	}
+};
+// 隐藏消息
+const hideNws = () => {
+	newsVisible.value = false;
+};
+
 const dutyFormRef = ref<RefType>();
 // 点击修改密码
-const onChangePwd = ()=>{
-  //  重置表单
-  if (dutyFormRef.value) {
-    dutyFormRef.value.resetFields();
-  }
-  showDutyDialog.value = true;
-}
+const onChangePwd = () => {
+	//  重置表单
+	if (dutyFormRef.value) {
+		dutyFormRef.value.resetFields();
+	}
+	showDutyDialog.value = true;
+};
 // 退出登录
-const onLogOut = ()=>{
-  if (telStatusInfo.value.isDutyOn) {
-    ElMessageBox({
-      closeOnClickModal: false,
-      closeOnPressEscape: false,
-      title: '提示',
-      message: '检测到您未签出,本次退出系统将自动签出, 是否继续?',
-      showCancelButton: true,
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      buttonSize: 'default',
-      draggable: true,
-      type: 'warning',
-      autofocus: false,
-      cancelButtonClass: 'default-button',
-      beforeClose: (action, instance, done) => {
-        if (action === 'confirm') {
-          instance.confirmButtonLoading = true;
-          instance.confirmButtonText = '退出中';
-          setTimeout(() => {
-            done();
-            setTimeout(() => {
-              instance.confirmButtonLoading = false;
-            }, 300);
-          }, 700);
-        } else {
-          done();
-        }
-      },
-    })
-        .then(async () => {
-          VoiceInterfaceObject.LogOut();
-          // offDuty().then(() => {
-          // 断开链接
-          // signalR.stop();
-          // 重置所有状态
-          usetelStatusStore.resetState();
-          // 清除缓存/token等
-          Local.clear();
-          Session.clear();
-          Cookie.clear();
-          // 使用 reload 时,不需要调用 resetRoute() 重置路由
-          window.location.reload();
-          // });
-        })
-        .catch(() => {});
-    return;
-  }
-  ElMessageBox({
-    closeOnClickModal: false,
-    closeOnPressEscape: false,
-    title: '提示',
-    message: '此操作将退出登录, 是否继续?',
-    showCancelButton: true,
-    confirmButtonText: '确定',
-    cancelButtonText: '取消',
-    buttonSize: 'default',
-    draggable: true,
-    type: 'warning',
-    autofocus: false,
-    cancelButtonClass: 'default-button',
-    beforeClose: (action, instance, done) => {
-      if (action === 'confirm') {
-        instance.confirmButtonLoading = true;
-        instance.confirmButtonText = '退出中';
-        setTimeout(() => {
-          done();
-          setTimeout(() => {
-            instance.confirmButtonLoading = false;
-          }, 300);
-        }, 700);
-      } else {
-        done();
-      }
-    },
-  })
-      .then(async () => {
-        // 清除缓存/token等
-        Local.clear();
-        Session.clear();
-        Cookie.clear();
-        // 使用 reload 时,不需要调用 resetRoute() 重置路由
-        window.location.reload();
-      })
-      .catch(() => {});
-}
+const onLogOut = () => {
+	if (telStatusInfo.value.isDutyOn) {
+		ElMessageBox({
+			closeOnClickModal: false,
+			closeOnPressEscape: false,
+			title: '提示',
+			message: '检测到您未签出,本次退出系统将自动签出, 是否继续?',
+			showCancelButton: true,
+			confirmButtonText: '确定',
+			cancelButtonText: '取消',
+			buttonSize: 'default',
+			draggable: true,
+			type: 'warning',
+			autofocus: false,
+			cancelButtonClass: 'default-button',
+			beforeClose: (action, instance, done) => {
+				if (action === 'confirm') {
+					instance.confirmButtonLoading = true;
+					instance.confirmButtonText = '退出中';
+					setTimeout(() => {
+						done();
+						setTimeout(() => {
+							instance.confirmButtonLoading = false;
+						}, 300);
+					}, 700);
+				} else {
+					done();
+				}
+			},
+		})
+			.then(async () => {
+				VoiceInterfaceObject.LogOut();
+				// offDuty().then(() => {
+				// 断开链接
+				// signalR.stop();
+				// 重置所有状态
+				usetelStatusStore.resetState();
+				// 清除缓存/token等
+				Local.clear();
+				Session.clear();
+				Cookie.clear();
+				// 使用 reload 时,不需要调用 resetRoute() 重置路由
+				window.location.reload();
+				// });
+			})
+			.catch(() => {});
+		return;
+	}
+	ElMessageBox({
+		closeOnClickModal: false,
+		closeOnPressEscape: false,
+		title: '提示',
+		message: '此操作将退出登录, 是否继续?',
+		showCancelButton: true,
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		buttonSize: 'default',
+		draggable: true,
+		type: 'warning',
+		autofocus: false,
+		cancelButtonClass: 'default-button',
+		beforeClose: (action, instance, done) => {
+			if (action === 'confirm') {
+				instance.confirmButtonLoading = true;
+				instance.confirmButtonText = '退出中';
+				setTimeout(() => {
+					done();
+					setTimeout(() => {
+						instance.confirmButtonLoading = false;
+					}, 300);
+				}, 700);
+			} else {
+				done();
+			}
+		},
+	})
+		.then(async () => {
+			// 清除缓存/token等
+			Local.clear();
+			Session.clear();
+			Cookie.clear();
+			// 使用 reload 时,不需要调用 resetRoute() 重置路由
+			window.location.reload();
+		})
+		.catch(() => {});
+};
 // 菜单搜索点击
 const onSearchClick = () => {
 	searchRef.value.openSearch();
@@ -433,7 +451,7 @@ const save = (formEl: FormInstance | undefined) => {
 					window.location.reload();
 					// 清楚缓存
 					Session.clear();
-          Cookie.clear();
+					Cookie.clear();
 					Local.clear();
 				}, 1000);
 			})
@@ -452,34 +470,40 @@ onMounted(() => {
 	if (Local.get('themeConfig')) {
 		initComponentSize();
 	}
+	getNumAndList();
+  signalR.SR.on('CircularRecord', (data: any) => {
+    // 小红点数量消息通知
+    console.log(data, '小红点数量消息通知');
+    getNumAndList();
+  });
 });
 </script>
 
 <style scoped lang="scss">
 .layout-navbars-breadcrumb-user {
-  height: 100%;
-  display: flex;
-  align-items: center;
-  justify-content: flex-end;
+	height: 100%;
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
 
 	&-link {
 		white-space: nowrap;
 		text-align: center;
-    flex:1;
+		flex: 1;
 		&-photo {
 			background-color: var(--hotline-bg-main-color);
 			border: 2px solid var(--el-color-primary);
-      margin-right: 5px;
+			margin-right: 5px;
 		}
 		&-photoBox {
 			display: flex;
 			justify-content: center;
-      align-items: center;
-      cursor: pointer;
-      &-name{
-        max-width: 150px;
-        overflow: hidden;
-      }
+			align-items: center;
+			cursor: pointer;
+			&-name {
+				max-width: 150px;
+				overflow: hidden;
+			}
 		}
 	}
 
@@ -488,13 +512,18 @@ onMounted(() => {
 		cursor: pointer;
 		color: var(--hotline-color-text-main);
 		&:hover {
-
 		}
 	}
-
+  .arrow {
+    transition: transform var(--el-transition-duration);
+    cursor: pointer;
+  }
+  .arrow.is-reverse {
+    transform: rotateZ(-180deg);
+  }
 	:deep(.el-dropdown) {
 		color: var(--hotline-color-text-main);
-		flex:1;
+		flex: 1;
 	}
 	.intensity {
 		.psdText {
@@ -538,20 +567,21 @@ onMounted(() => {
 }
 </style>
 <style lang="scss">
-.user-info-box{
-  &-content{
-    &-item{
-      display: flex;
-      align-content: center;
-      &-title{
-        display: inline-block;
-        width: 70px;
-        text-align: right;
-      }
-      &-content{
-        flex:1;
-      }
-    }
-  }
+.user-info-box {
+	&-content {
+		&-item {
+			display: flex;
+			align-content: center;
+			&-title {
+				display: inline-block;
+				width: 70px;
+				text-align: right;
+			}
+			&-content {
+				flex: 1;
+			}
+		}
+	}
 }
+
 </style>

+ 51 - 81
src/layout/navBars/breadcrumb/userNews.vue

@@ -1,39 +1,32 @@
 <template>
 	<div class="layout-navBars-breadcrumb-user-news">
-		<el-popover placement="bottom" v-model:visible="visible" trigger="click" popper-class="layout-navBars-breadcrumb-user-news" transition="el-zoom-in-top" :width="300" :persistent="false">
-			<template #reference>
-				<el-badge :value="messageCount" :max="99" :hidden="messageHidden">
-					<SvgIcon name="ele-Bell" size="18px"/>
-				</el-badge>
-			</template>
-			<template #default>
-				<div class="head-box">
-					<div class="head-box-title">通知</div>
-<!--					<div class="head-box-btn" v-if="state.newsList.length > 0" @click="onAllReadClick">全部已读</div>-->
-				</div>
-				<div class="content-box">
-					<template v-if="state.newsList.length > 0">
-						<div class="content-box-item" v-for="(v, k) in state.newsList" :key="k" @click="linkNews(v)">
-							<div class="text-no-wrap">{{ v.title }}</div>
-							<div class="content-box-msg">
-                <div class="text-ellipsis2" v-html="v.content"></div>
-							</div>
-							<div class="content-box-time">{{ formatDate(v.circularTime,'YYYY-mm-dd HH:MM:SS') }}</div>
-						</div>
-					</template>
-					<el-empty description="暂无通知" v-else></el-empty>
+		<div class="head-box">
+			<div class="head-box-title">通知</div>
+			<!--					<div class="head-box-btn" v-if="state.newsList.length > 0" @click="onAllReadClick">全部已读</div>-->
+		</div>
+		<div class="content-box">
+			<template v-if="state.newsList.length > 0">
+				<div class="content-box-item" v-for="(v, k) in state.newsList" :key="k" @click="linkNews(v)">
+					<div class="text-no-wrap">{{ v.title }}</div>
+					<div class="content-box-msg">
+						<div class="text-ellipsis2" v-html="v.content"></div>
+					</div>
+					<div class="content-box-time">{{ formatDate(v.circularTime, 'YYYY-mm-dd HH:MM:SS') }}</div>
 				</div>
 			</template>
-		</el-popover>
+			<el-empty description="暂无通知" v-else></el-empty>
+		</div>
 	</div>
 </template>
 
 <script setup lang="ts" name="layoutBreadcrumbUserNews">
-import {reactive, computed, ref, onMounted} from 'vue';
+import { reactive, onMounted } from 'vue';
 import signalR from '/@/utils/signalR';
 import { formatDate } from '/@/utils/formatTime';
-import {useRouter} from 'vue-router';
-import {megcount,muquery} from '/@/api/auxiliary/notice'
+import { useRouter } from 'vue-router';
+import { muquery } from '/@/api/auxiliary/notice';
+
+const emit = defineEmits(['hideNws']);
 
 const state = reactive({
 	newsList: [],
@@ -41,63 +34,41 @@ const state = reactive({
 // 全部已读点击
 const onAllReadClick = () => {
 	state.newsList = [];
-	messageCount.value = 0;
 };
 
-const messageCount = ref<number>(0); //消息数量
-const messageHidden = computed(() => {
-	//消息是否隐藏
-	return messageCount.value === 0;
-});
-
 // 获取数量
-const getNumAndList = async ()=>{
-  try {
-    const [megcountRes,muqueryRes] = await Promise.all([megcount(),muquery({circularState:2,IsRead:false,PageIndex:1,PageSize:999})])
-    messageCount.value = megcountRes.result?.personCount+megcountRes.result?.orgCount;
-    state.newsList = muqueryRes.result?.items ?? [];
-  }catch (e) {
-    console.log(e,'获取消息数量失败')
-  }
-}
+const getNumAndList = async () => {
+	try {
+		const [muqueryRes] = await Promise.all([muquery({ circularState: 2, IsRead: false, PageIndex: 1, PageSize: 999 })]);
+		state.newsList = muqueryRes.result?.items ?? [];
+	} catch (e) {
+		console.log(e, '获取消息数量失败');
+	}
+};
 // 点击消息通知
 const router = useRouter();
-const visible = ref<boolean>(false); //是否显示消息
-const linkNews = (v:any)=>{
-  router.push({
-    name: 'auxiliaryNoticeDetail',
-    params: {
-      id: v.id,
-      isRead: 1,
-      tagsViewName:'通知详情',
-    },
+const linkNews = (v: any) => {
+	emit('hideNws');
+	router.push({
+		name: 'auxiliaryNoticeDetail',
+		params: {
+			id: v.id,
+			isRead: 1,
+			tagsViewName: '通知详情',
+		},
+	});
+};
+onMounted(() => {
+	getNumAndList();
+  signalR.SR.on('CircularRecord', (data: any) => {
+    // 小红点数量消息通知
+    console.log(data, '小红点数量消息通知');
+    getNumAndList();
   });
-  visible.value = false;
-}
-onMounted(()=>{
-  getNumAndList();
-  if(signalR.SR){
-    signalR.SR.on('CircularRecord', (data: any) => {
-      // 小红点数量消息通知
-      console.log(data, '小红点数量消息通知');
-      getNumAndList();
-    });
-  }else{
-    //  signalR 初始化signalr
-    signalR.init();
-    // 加入分组
-    signalR.joinGroup('CallCenter');
-    console.log('signalR.SR不存在');
-    signalR.SR.on('CircularRecord', (data: any) => {
-      // 小红点数量消息通知
-      console.log(data, '小红点数量消息通知');
-      getNumAndList();
-    });
-  }
-})
+});
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 .layout-navBars-breadcrumb-user-news {
 	.head-box {
 		display: flex;
@@ -122,14 +93,13 @@ onMounted(()=>{
 
 	.content-box {
 		font-size: 13px;
-
 		.content-box-item {
 			padding: 12px 10px 5px 10px;
-      border-radius: 5px;
-      &:hover{
-        background-color: var(--el-color-primary-light-9);
-        cursor: pointer;
-      }
+			border-radius: 5px;
+			&:hover {
+				background-color: var(--el-color-primary-light-9);
+				cursor: pointer;
+			}
 			&:last-of-type {
 				padding-bottom: 12px;
 			}

+ 10 - 3
src/layout/navMenu/subItem.vue

@@ -1,20 +1,27 @@
 <template>
 	<template v-for="val in chils">
 		<!-- 默认样式 -->
-		<el-sub-menu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0 && isMobile">
+<!--		<el-sub-menu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0 && isMobile">
 			<template #title>
 				<SvgIcon :name="val.meta.icon" />
 				<span>{{ val.meta.title }}</span>
 			</template>
 			<sub-item :chil="val.children" />
 		</el-sub-menu>
-		<!-- pop展示第三级菜单 -->
+		&lt;!&ndash; pop展示第三级菜单 &ndash;&gt;
 		<sub-three-item v-else-if="val.children && val.children.length > 0 && !isMobile" :key="val.path" :chil="val.children">
 			<template #header>
 				<SvgIcon :name="val.meta.icon" />
 				<span>{{ val.meta.title }}</span>
 			</template>
-		</sub-three-item>
+		</sub-three-item>-->
+     <el-sub-menu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0">
+			<template #title>
+				<SvgIcon :name="val.meta.icon" />
+				<span>{{ val.meta.title }}</span>
+			</template>
+			<sub-item :chil="val.children" />
+		</el-sub-menu>
 		<template v-else>
 			<el-menu-item :index="val.path" :key="val.path">
 				<template v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">

+ 24 - 0
src/stores/appConfig.ts

@@ -24,4 +24,28 @@ export const useAppConfig = defineStore('AppConfig', {
 			};
 		},
 	},
+	// 开启数据缓存
+	persist: {
+		enabled: true,
+		strategies: [
+			{
+				//key的名称
+				key: `${import.meta.env.VITE_STORAGE_NAME}_appConfig`,
+				//更改默认存储,我更改为localStorage
+				storage: localStorage,
+				// 可以选择哪些进入local存储,这样就不用全部都进去存储了
+				// 默认是全部进去存储
+			},
+			// {
+			// 	key: `${import.meta.env.VITE_STORAGE_NAME}_base`,
+			// 	storage: localStorage,
+			// 	paths: ['isDutyOn','isRest']
+			// },
+			// {
+			// 	key: `${import.meta.env.VITE_STORAGE_NAME}_base2`,
+			// 	storage: sessionStorage,
+			// 	paths: ['telsNo']
+			// },
+		]
+	}
 });

+ 0 - 2
src/types/mitt.d.ts

@@ -12,6 +12,4 @@ declare type MittType = {
 	onCurrentContextmenuClick?: any; // tagsview 右键菜单每项点击时
 	scrollTopEmit?: object; //点击分页跳转滚动到顶部
 	clearCache?: any; //清除某个页面的缓存
-	startTalkTime?: any; //开始通话时间
-	endTalkTime?: any; //结束通话时间
 };

+ 3 - 1
src/utils/signalR.ts

@@ -73,7 +73,9 @@ export default {
 			await this.SR.invoke('JoinGroupAsync', {GroupName:groupName});
 		} else {
 			await this.start().then(async () => {
-				await this.SR.invoke('JoinGroupAsync', {GroupName:groupName});
+				setTimeout(async () => {
+					await this.SR.invoke('JoinGroupAsync', {GroupName:groupName});
+				},500)
 			}).catch((err) => {
 				console.log(err);
 			})

+ 0 - 4
src/views/visualizing/demo1.vue

@@ -1384,10 +1384,6 @@ onMounted(async () => {
 	await initSourceProportion(); // 来源占比分析
 	await initComplaintHotWords(); // 投诉热词
 	await initEchartsResize(); //
-  //  signalR 初始化signalr
-  signalR.init();
-  // 加入分组
-  await signalR.joinGroup('BigScreenDataShow');
   signalR.SR.on('Send', (data: any) => {
     // 加入分组成功
     console.log(data, '加入分组成功');