Browse Source

Merge branch 'master' into yibin

zhangchong 1 year ago
parent
commit
5013d7c5d3
35 changed files with 951 additions and 145 deletions
  1. 11 0
      src/api/statistics/call.ts
  2. 0 0
      src/api/statistics/knowledge.ts
  3. 22 0
      src/api/statistics/order.ts
  4. 27 0
      src/api/todo/voiceAssistant.ts
  5. 1 1
      src/components/ImgVerify/hooks.ts
  6. 0 2
      src/components/ProcessTimeLine/index.vue
  7. 2 2
      src/layout/navBars/breadcrumb/telControl.vue
  8. 1 1
      src/stores/themeConfig.ts
  9. 7 0
      src/theme/element.scss
  10. 1 1
      src/utils/toolsValidate.ts
  11. 1 1
      src/views/business/order/index.vue
  12. 0 18
      src/views/business/visit/component/Visit-detail.vue
  13. 1 1
      src/views/home/component/ToDo.vue
  14. 4 15
      src/views/login/component/Account.vue
  15. 273 36
      src/views/login/component/Mobile.vue
  16. 21 16
      src/views/login/index.vue
  17. 15 4
      src/views/smartVisit/components/Smart-visit-Detail.vue
  18. 124 0
      src/views/statistics/call/hotNumber.vue
  19. 1 1
      src/views/statistics/call/talkTime.vue
  20. 1 1
      src/views/statistics/order/acceptTopTen.vue
  21. 2 2
      src/views/statistics/order/hotspotCount.vue
  22. 131 0
      src/views/statistics/order/hotspotSubclass.vue
  23. 1 1
      src/views/statistics/order/orgDelay.vue
  24. 1 1
      src/views/statistics/order/orgHotspot.vue
  25. 1 1
      src/views/statistics/order/specialTable.vue
  26. 1 1
      src/views/statistics/order/specials.vue
  27. 104 0
      src/views/statistics/order/visitCount.vue
  28. 1 1
      src/views/statistics/order/visitDiscontent.vue
  29. 1 1
      src/views/statistics/order/visitTable.vue
  30. 1 1
      src/views/system/menu/index.vue
  31. 155 0
      src/views/todo/countersign/index.vue
  32. 1 1
      src/views/todo/seats/accept/Expand-form.vue
  33. 17 29
      src/views/todo/seats/accept/Voice-assistant.vue
  34. 9 6
      src/views/todo/seats/accept/index.vue
  35. 12 0
      src/views/todo/visit/index.vue

+ 11 - 0
src/api/statistics/call/index.ts → src/api/statistics/call.ts

@@ -58,4 +58,15 @@ export const callPeriod = (params: object) => {
 		method: 'get',
 		params,
 	});
+}
+/**
+ * @description 热线号码统计
+ * @param {object} params
+ */
+export const callHotline = (params: object) => {
+	return request({
+		url: `/api/v1/BiCall/gateway-query`,
+		method: 'get',
+		params,
+	});
 }

+ 0 - 0
src/api/statistics/knowledge/index.ts → src/api/statistics/knowledge.ts


+ 22 - 0
src/api/statistics/order/index.ts → src/api/statistics/order.ts

@@ -112,4 +112,26 @@ export const departmentTopTen = (params: object) => {
 		method: 'get',
 		params,
 	});
+}
+/**
+ * @description 回访量统计
+ * @param {object} params
+ */
+export const departmentVisit = (params: object) => {
+	return request({
+		url: `/api/v1/BiOrder/visit-measure-statistics`,
+		method: 'get',
+		params,
+	});
+}
+/**
+ * @description 热点类型小类统计
+ * @param {object} params
+ */
+export const departmentHotSmall = (params: object) => {
+	return request({
+		url: `/api/v1/BiOrder/hotspot-statistics`,
+		method: 'get',
+		params,
+	});
 }

+ 27 - 0
src/api/todo/voiceAssistant.ts

@@ -0,0 +1,27 @@
+/*
+ * @Author: zc
+ * @description 坐席辅助
+ */
+import request from '@/utils/request';
+/**
+ * @description 链接坐席辅助
+ * @param {string} telNo
+ */
+export const voiceAssistant = (telNo: string) => {
+  return request({
+    url: `users/getUserByAgentId/${telNo}`,
+    method: 'get',
+    baseURL:import.meta.env.VITE_VOICE_ASSISTANT_API_URL // 请求地址  可以在不同url中修改
+  });
+}
+/**
+ * @description 获取识别内容
+ * @param {string} callId
+ */
+export const voiceAssistantContent = (callId: string) => {
+  return request({
+    url: `/monitor/remote/details/${callId}`,
+    method: 'get',
+    baseURL:import.meta.env.VITE_VOICE_ASSISTANT_API_URL
+  });
+}

+ 1 - 1
src/components/ImgVerify/hooks.ts

@@ -43,7 +43,7 @@ function randomColor(min: number, max: number) {
 
 function draw(dom: HTMLCanvasElement, width: number, height: number) {
 	let imgCode = '';
-	const Random_STRING: string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+	const Random_STRING: string = '23456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 	const ctx = dom.getContext('2d');
 	if (!ctx) return imgCode;
 

+ 0 - 2
src/components/ProcessTimeLine/index.vue

@@ -1,5 +1,4 @@
 <template>
-	<div>
 		<el-timeline class="time-line">
 			<el-timeline-item v-for="(item, index) in props.data" :key="index" :type="item.type" size="large" hollow hide-timestamp>
 				<!-- 左侧展示 -->
@@ -93,7 +92,6 @@
 				</el-collapse-transition>
 			</el-timeline-item>
 		</el-timeline>
-	</div>
 </template>
 
 <script setup lang="ts" name="processTimeLine">

+ 2 - 2
src/layout/navBars/breadcrumb/telControl.vue

@@ -499,6 +499,7 @@ import dayjs from 'dayjs';
 import axios from 'axios';
 import { useSocket } from '@/utils/websocket';
 import mittBus from '@/utils/mitt';
+import { voiceAssistant } from "@/api/todo/voiceAssistant";
 // 引入组件
 const CommonAdvice = defineAsyncComponent(() => import('@/components/CommonAdvice/index.vue')); // 常用意见
 const AnnexList = defineAsyncComponent(() => import('@/components/AnnexList/index.vue'));
@@ -1474,8 +1475,7 @@ const connectVoiceAssistant = async (telNo: string) => {
 		socket.value = null;
 	}
 	try {
-		const { data } = await axios.get(`${import.meta.env.VITE_VOICE_ASSISTANT_API_URL}/users/getUserByAgentId/${telNo}`);
-		const { result } = data;
+		const { result } = await voiceAssistant(telNo);
 		const uid = `8#User-${result.uid}`;
 		const subscribe = `/trans/${result.orgCode}/${result.groupUid}/${uid}`;
 		socket.value = useSocket(import.meta.env.VITE_VOICE_ASSISTANT_SOCKET_URL, { uid, subscribe });

+ 1 - 1
src/stores/themeConfig.ts

@@ -130,7 +130,7 @@ export const useThemeConfig = defineStore('themeConfig', {
 			 * 全局网站标题 / 副标题
 			 */
 			// 网站主标题(菜单导航、浏览器当前网页标题)
-			globalTitle: '',
+			globalTitle: '宜宾市民热线服务系统',
 			// 网站副标题(登录页顶部文字)
 			globalViceTitle: '账户登录',
 			// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn

+ 7 - 0
src/theme/element.scss

@@ -23,6 +23,9 @@
 // .el-input {
 // 	height: 100%;
 // }
+//.el-input,.el-radio__input,.el-checkbox__input,.el-input__inner {
+//	--el-border-color: var(--el-border-color-darker);
+//}
 // 菜单搜索
 .el-autocomplete-suggestion__wrap {
 	max-height: 280px !important;
@@ -268,6 +271,10 @@
 	th.el-table__cell {
 		background-color: var(--hotline-bg-main-color) !important;
 	}
+	thead{
+		--el-table-border-color: var(--el-border-color-light);
+		--el-table-border: 1px solid var(--el-table-border-color);
+	}
 }
 .el-table-v2__header-row {
 	background-color: var(--hotline-bg-main-color) !important;

+ 1 - 1
src/utils/toolsValidate.ts

@@ -188,7 +188,7 @@ export function verifyNumberCnUppercase(val: any, unit = '仟佰拾亿仟佰拾
  */
 export function verifyPhone(val: string) {
 	// false: 手机号码不正确
-	return /^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0|1,5-9]))\d{8}$/.test(val);
+	return /^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(val);
 }
 
 /**

+ 1 - 1
src/views/business/order/index.vue

@@ -209,7 +209,7 @@
 						<el-button link type="success" @click="onOrderEdit(row)" title="编辑工单" v-if="row.canEdit" v-auth="'business:order:edit'">
 							修改
 						</el-button>
-						<order-detail :order="row" @updateList="queryList" v-if="row.workflowId" />
+						<order-detail :order="row" @updateList="queryList"/>
 					</template>
 				</el-table-column>
 				<template #empty>

+ 0 - 18
src/views/business/visit/component/Visit-detail.vue

@@ -129,13 +129,6 @@
 								<el-row v-for="item in state.ruleForm.visitDetails" :key="item.id" :gutter="10">
 									<!-- 务员评价 -->
 									<template v-if="item.visitTarget === 10">
-										<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-											<el-form-item label="语音评价">
-												<el-radio-group v-model="item.voiceEvaluate" disabled>
-													<el-radio :label="item.key" v-for="item in viceEvaluate" :key="item.key">{{ item.value }}</el-radio>
-												</el-radio-group>
-											</el-form-item>
-										</el-col>
 										<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 											<el-form-item label="话务员评价">
 												<el-radio-group v-model="item.seatEvaluate" disabled>
@@ -197,17 +190,6 @@
 									<el-row v-for="(item, index) in state.ruleForm.visitDetails" :key="item.id" :gutter="10">
 										<!-- 务员评价 -->
 										<template v-if="item.visitTarget === 10">
-											<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-												<el-form-item
-													label="语音评价"
-													:prop="`visitDetails.${index}.voiceEvaluate`"
-													:rules="[{ required: notGetThrough, message: '请选择语音评价', trigger: 'change' }]"
-												>
-													<el-radio-group v-model="item.voiceEvaluate">
-														<el-radio :label="item.key" v-for="item in viceEvaluate" :key="item.key">{{ item.value }}</el-radio>
-													</el-radio-group>
-												</el-form-item>
-											</el-col>
 											<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 												<el-form-item
 													label="话务员评价"

+ 1 - 1
src/views/home/component/ToDo.vue

@@ -114,7 +114,7 @@
 					<template #default="{ row }">
 						<el-button link type="success" @click="onOrderEdit(row)" title="编辑工单" v-if="row.canEdit" v-auth="'todo:seats:edit'"> 修改 </el-button>
 						<el-button link type="primary" @click="onSign(row)" title="签收工单" v-if="row.canSign" v-auth="'todo:seats:sign'"> 签收 </el-button>
-						<order-detail :order="row" @updateList="handleChange('1')" v-if="row.workflowId" />
+						<order-detail :order="row" @updateList="handleChange('1')"/>
 					</template>
 				</el-table-column>
 			</template>

+ 4 - 15
src/views/login/component/Account.vue

@@ -37,6 +37,7 @@
 		<motion :delay="300">
 			<el-form-item
 				prop="verifyCode"
+        class="mb30"
 				:rules="[
 					{ required: true, message: '请输入验证码', trigger: 'blur' },
 					{
@@ -72,9 +73,7 @@
 			</el-form-item>
 		</motion>
     <motion :delay="500">
-       <div class="mt10">
         运营管理系统 <span class="color-danger font-bold">v5.0</span>
-      </div>
     </motion>
 		<motion :delay="500">
 			<div class="login-msg">
@@ -275,24 +274,14 @@ const signInSuccess = (isNoPower: boolean | undefined) => {
 
 .login-content-form {
 	margin-top: 20px;
-	font-size: 16px;
+  font-size: var(--el-font-size-medium);
 
 	:deep(.el-input--large) {
-		font-size: 16px;
+    font-size: var(--el-font-size-medium);
 	}
 
 	:deep(.el-form-item__error) {
-		font-size: 14px;
-	}
-
-	.login-content-password {
-		display: inline-block;
-		width: 20px;
-		cursor: pointer;
-
-		&:hover {
-			color: #909399;
-		}
+    font-size: var(--el-font-size-medium);
 	}
 
 	.login-content-code {

+ 273 - 36
src/views/login/component/Mobile.vue

@@ -1,69 +1,290 @@
 <template>
-	<el-form size="large" class="login-content-form">
-		<el-form-item class="login-animation1">
-			<el-input type="text" placeholder="请输入手机号" v-model="state.ruleForm.userName" clearable autocomplete="off">
+	<el-form size="large" class="login-content-form" ref="ruleFormRef" :model="state.ruleForm" @submit.native.prevent>
+		<el-form-item
+			prop="userName"
+			class="mb30"
+			:rules="[
+				{ required: true, message: '请输入手机号', trigger: 'blur' },
+				{ validator: checkPhone, trigger: 'blur' },
+			]"
+		>
+			<el-input placeholder="请输入手机号" v-model="state.ruleForm.userName" clearable autocomplete="off" class="inputDeep">
 				<template #prefix>
-					<i class="iconfont icon-dianhua el-input__icon"></i>
+					<SvgIcon name="ele-Phone" class="el-input__icon" />
 				</template>
 			</el-input>
 		</el-form-item>
-		<el-form-item class="login-animation2">
+		<el-form-item prop="password" class="mb30" :rules="[{ required: true, message: '请输入短信验证码', trigger: 'blur' }]">
 			<el-col :span="15">
-				<el-input type="text" maxlength="4" placeholder="请输入验证码" v-model="state.ruleForm.code" clearable autocomplete="off">
+				<el-input
+					type="text"
+					maxlength="4"
+					placeholder="请输入短信验证码"
+					v-model="state.ruleForm.code"
+					clearable
+					autocomplete="off"
+					class="inputDeep"
+				>
 					<template #prefix>
-						<el-icon class="el-input__icon"><ele-Position /></el-icon>
+						<SvgIcon name="ele-ChatDotSquare" class="el-input__icon" />
 					</template>
 				</el-input>
 			</el-col>
 			<el-col :span="1"></el-col>
 			<el-col :span="8">
-				<el-button class="login-content-code">获取验证码</el-button>
+				<el-button class="login-content-code" :disabled="isDisabled" @click="getIdentifyCodeBtn">{{
+					isDisabled ? count + 's后重新获取' : click
+				}}</el-button>
 			</el-col>
 		</el-form-item>
-		<el-form-item class="login-animation3">
-			<el-button round type="primary" class="login-content-submit">
-				<span>登 录</span>
-			</el-button>
+		<el-form-item
+			prop="verifyCode"
+			:rules="[
+				{ required: true, message: '请输入验证码', trigger: 'blur' },
+				{
+					validator: validatePass,
+					trigger: 'blur',
+				},
+			]"
+      class="mb30"
+		>
+			<el-col :span="15">
+				<el-input
+					type="text"
+					maxlength="4"
+					class="inputDeep"
+					placeholder="验证码不区分大小写"
+					v-model="state.ruleForm.verifyCode"
+					@keyup.enter="onSignIn(ruleFormRef)"
+					clearable
+					autocomplete="off"
+				>
+					<template #prefix>
+						<SvgIcon name="iconfont icon-quanxian" class="el-input__icon" />
+					</template>
+				</el-input>
+			</el-col>
+			<el-col :span="8" :offset="1" class="flex">
+				<ReImageVerify v-model:code="verifyCode" />
+			</el-col>
+		</el-form-item>
+		<el-form-item>
+			<el-button type="primary" class="login-content-submit" round @click="onSignIn(ruleFormRef)" :loading="state.loading">登录</el-button>
 		</el-form-item>
-		<div class="font12 mt30 login-animation4 login-msg">
-			* 温馨提示:建议使用谷歌、Microsoft Edge,版本 79.0.1072.62 及以上浏览器,360浏览器请使用极速模式
-		</div>
+    <motion :delay="500">
+      <div>
+        运营管理系统 <span class="color-danger font-bold">v5.0</span>
+      </div>
+    </motion>
+    <motion :delay="500">
+      <div class="login-msg">
+        <div>联系管理员<b>重置密码</b></div>
+        <!-- <el-button link type="primary" class="font16" @click="forgetPwd">忘记密码</el-button> -->
+      </div>
+    </motion>
 	</el-form>
 </template>
 
 <script setup lang="ts" name="loginMobile">
-import { reactive } from 'vue';
-// 定义接口来定义对象的类型
-interface LoginMobileState {
-	userName: any;
-	code: string | number | undefined;
-}
+import { reactive, defineAsyncComponent, ref, computed } from 'vue';
+import { ElNotification, FormInstance } from 'element-plus';
+import { throttle } from '@/utils/tools';
+import { JSEncrypt } from 'jsencrypt';
+import { signIn } from '@/api/login';
+import { Cookie, Local, Session } from '@/utils/storage';
+import { storeToRefs } from 'pinia';
+import { useThemeConfig } from '@/stores/themeConfig';
+import { useRoute, useRouter } from 'vue-router';
+import { initFrontEndControlRoutes } from '@/router/frontEnd';
+import { initBackEndControlRoutes } from '@/router/backEnd';
+import { formatAxis } from '@/utils/formatTime';
+import { NextLoading } from '@/utils/loading';
+import { verifyPhone } from '@/utils/toolsValidate';
+import Motion from "@/utils/motion";
 
-// 定义对象与类型
-const ruleForm: LoginMobileState = {
-	userName: '',
-	code: '',
-};
+const ReImageVerify = defineAsyncComponent(() => import('@/components/ImgVerify/index.vue'));
 
 // 定义变量内容
 const state = reactive({
-	ruleForm
+	ruleForm: {
+		userName: '',
+		code: '',
+		verifyCode: '',
+	},
+	loading: false,
 });
 
+// 验证码
+const verifyCode = ref<string>(''); // 验证码
+// 验证手机号
+const validatePass = (rule: any, value: any, callback: any) => {
+	if (value === '') {
+		callback(new Error('请输入验证码'));
+	} else {
+		if (state.ruleForm.verifyCode !== '') {
+			if (state.ruleForm.verifyCode.toUpperCase() !== verifyCode.value.toUpperCase()) {
+				callback(new Error('验证码错误,请重新输入'));
+			}
+		}
+		callback();
+	}
+};
+const count = ref(60);
+const click = ref('获取验证码');
+const isDisabled = ref(false);
+const checkPhone = (rule, value, callback) => {
+	if (!value) {
+		return callback(new Error('手机号不能为空'));
+	} else {
+		if (verifyPhone(value)) {
+			callback();
+		} else {
+			return callback(new Error('请输入正确的手机号'));
+		}
+	}
+};
+const getIdentifyCodeBtn = () => {
+  if(!state.ruleForm.userName) {
+    ruleFormRef.value?.validateField('userName');
+    return;
+  }
+  if (!verifyPhone(state.ruleForm.userName)) {
+    ruleFormRef.value?.validateField('userName');
+    return;
+  }
+  // state.loading = true;
+	countDown();
+};
+// 倒计时
+const countDown = () => {
+	if (count.value === 0) {
+		isDisabled.value = false;
+		click.value = '获取验证码';
+		count.value = 60;
+		return;
+	} else {
+		count.value--;
+		click.value = count.value + 's后重新获取';
+		isDisabled.value = true;
+		setTimeout(() => {
+			countDown();
+		}, 1000);
+	}
+};
+
+const ruleFormRef = ref<FormInstance>(); // 表单ref
+const storesThemeConfig = useThemeConfig(); // 主题配置
+const { themeConfig } = storeToRefs(storesThemeConfig); // 主题配置
+const route = useRoute(); // 路由
+const router = useRouter(); // 路由
+// 登录
+const onSignIn = throttle(async (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	await formEl.validate((valid: boolean) => {
+		if (!valid) return;
+		state.loading = true;
+		// 新建一个JSEncrypt对象
+		const encryptor = new JSEncrypt({ default_key_size: '2048' });
+		// 设置公钥
+		const publicKey =
+			'-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgw+/x6IQPkH0A4eoF63jkLThsOXWyNBdcL9LATGy/G1yTHOr1RyKJB//iNug+V8DIoIHuFTlhgLHDbSqxvRWMONxIIF289riS6bDI4Ox/pFmOfmElFRk0lKGihaTE2Aefd6g/N+RfLLaHWztY+/voVeDTiOIw9y3tokIxjKwuJ/mQ66MkKh78AqQjjSD/3jcBP8ZhMyCJOK9XQcqvhD6WBFWkxlAqKOWggDU7YohfrbNkg3bd0oGE6zCE2EHhkcQbzGCh3lu1zf4TfKMXD+PPrr5JWDNYQTXFQklqgae+Puge7xxZGYRoi5YpIUnkQGm6zpPxhIOdxlz+Yb5geSJUQIDAQAB-----END PUBLIC KEY-----';
+		encryptor.setPublicKey(publicKey); // publicKey为公钥
+		// 加密数据
+		const submitObj = {
+			username: encryptor.encrypt(state.ruleForm.username),
+			password: encryptor.encrypt(state.ruleForm.password),
+		};
+		signIn(submitObj)
+			.then(async (res: any) => {
+				//登录
+				// 存储 token 到浏览器缓存
+				Cookie.set('token', res.result);
+				if (!themeConfig.value.isRequestRoutes) {
+					// 前端控制路由,2、请注意执行顺序
+					const isNoPower = await initFrontEndControlRoutes();
+					signInSuccess(isNoPower);
+				} else {
+					// 模拟后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
+					// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
+					const isNoPower = await initBackEndControlRoutes();
+					// 执行完 initBackEndControlRoutes,再执行 signInSuccess
+					signInSuccess(isNoPower);
+				}
+			})
+			.catch(() => {
+				state.loading = false;
+			});
+	});
+}, 1000);
+// 登录成功后的跳转
+// 时间获取
+const currentTime = computed(() => {
+	return formatAxis(new Date());
+});
+const signInSuccess = (isNoPower: boolean | undefined) => {
+	if (isNoPower) {
+		state.loading = false;
+		ElNotification({
+			title: '提示',
+			message: '抱歉,您没有登录权限,请联系管理员',
+			type: 'warning',
+		});
+		Session.clear();
+		Cookie.clear();
+		Local.clear();
+	} else {
+		// 初始化登录成功时间问候语
+		let currentTimeInfo = currentTime.value;
+		// 登录成功,跳到转首页
+		/*// 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
+    if (route.query?.redirect) {
+      router.push({
+        path: <string>route.query?.redirect,
+        query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
+      });
+    } else {
+      router.push('/');
+    }*/
+		router.push('/');
+		// 设置登录成功后的时间问候语
+		Cookie.set('userName', state.ruleForm.username);
+		// 登录成功提示
+		// 关闭 loading
+		state.loading = true;
+		const signInText = '欢迎回来!';
+		ElNotification({
+			type: 'success',
+			title: `${currentTimeInfo}`,
+			message: `${signInText}`,
+		});
+		NextLoading.start();
+	}
+};
 </script>
 
 <style scoped lang="scss">
+.inputDeep {
+	:deep(.el-input__wrapper) {
+		box-shadow: 0 0 0 0 var(--el-input-border-color, var(--el-border-color)) inset;
+		border-radius: 0;
+		border-bottom: 1px solid var(--el-input-border-color, var(--el-border-color));
+	}
+	:deep(.el-form-item.is-error .el-input__wrapper.is-focus) {
+		box-shadow: 0 0 0 0 var(--el-input-border-color, var(--el-border-color)) inset;
+	}
+}
 .login-content-form {
+	font-size: var(--el-font-size-medium);
 	margin-top: 20px;
-	@for $i from 1 through 4 {
-		.login-animation#{$i} {
-			opacity: 0;
-			animation-name: error-num;
-			animation-duration: 0.5s;
-			animation-fill-mode: forwards;
-			animation-delay: calc($i/10) + s;
-		}
+
+	:deep(.el-input--large) {
+		font-size: var(--el-font-size-medium);
+	}
+
+	:deep(.el-form-item__error) {
+		font-size: var(--el-font-size-medium);
 	}
+
 	.login-content-code {
 		width: 100%;
 		padding: 0;
@@ -74,8 +295,24 @@ const state = reactive({
 		font-weight: 300;
 		margin-top: 15px;
 	}
+	.login-content-submit {
+		width: 100%;
+		margin-top: 45px;
+		height: 50px;
+		border-radius: 30px;
+		font-size: 16px;
+		letter-spacing: 5px;
+	}
 	.login-msg {
-		color: var(--el-text-color-placeholder);
+    margin-top: 10px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    color: var(--el-color-primary);
+    b {
+      color: #999;
+      padding-left: 4px;
+    }
 	}
 }
 </style>

+ 21 - 16
src/views/login/index.vue

@@ -6,21 +6,23 @@
 				<Motion>
 					<h4 class="login-content-title">{{ getThemeConfig.globalViceTitle }}</h4>
 				</Motion>
-				<div v-if="!state.isScan">
-					<el-tabs v-model="state.tabsActiveName">
-						<!-- <el-tab-pane label="账号密码登录" name="account"> -->
-						<account />
-						<!-- </el-tab-pane> -->
-						<!-- <el-tab-pane label="手机号登录" name="mobile">
-							<Mobile />
-						</el-tab-pane> -->
-					</el-tabs>
-				</div>
-				<!-- <Scan v-if="isScan" />
-				<div class="login-content-main-scan" @click="isScan = !isScan">
-					<i class="iconfont" :class="isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i>
+        <Motion>
+<!--          <div v-if="!state.isScan">-->
+            <el-tabs v-model="state.tabsActiveName">
+              <el-tab-pane label="账号登录" name="account">
+                <account />
+              </el-tab-pane>
+              <el-tab-pane label="手机号登录" name="mobile">
+                <Mobile />
+              </el-tab-pane>
+            </el-tabs>
+<!--          </div>-->
+        </Motion>
+<!--				<Scan v-if="state.isScan" />
+				<div class="login-content-main-scan" @click="state.isScan = !state.isScan">
+					<i class="iconfont" :class="state.isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i>
 					<div class="login-content-main-scan-delta"></div>
-				</div> -->
+				</div>-->
 			</div>
 		</div>
 	</div>
@@ -41,8 +43,8 @@ interface LoginState {
 
 // 引入组件
 const Account = defineAsyncComponent(() => import('@/views/login/component/Account.vue'));
-// const Mobile = defineAsyncComponent(() => import('@/views/login/component/Mobile.vue'));
-// const Scan = defineAsyncComponent(() => import('@/views/login/component/Scan.vue'));
+const Mobile = defineAsyncComponent(() => import('@/views/login/component/Mobile.vue'));
+const Scan = defineAsyncComponent(() => import('@/views/login/component/Scan.vue'));
 
 const storesThemeConfig = useThemeConfig();
 const { themeConfig } = storeToRefs(storesThemeConfig);
@@ -62,6 +64,9 @@ onMounted(async () => {
 </script>
 
 <style scoped lang="scss">
+:deep(.el-tabs__item){
+  font-size: var(--el-font-size-medium);
+}
 .login-container {
 	position: relative;
 	background-image: v-bind(bgImg);

+ 15 - 4
src/views/smartVisit/components/Smart-visit-Detail.vue

@@ -38,16 +38,27 @@
         </template>
       </el-table-column>
       <el-table-column prop="order.actualOpinion" label="办件结果" show-overflow-tooltip width="150"></el-table-column>
-      <el-table-column label="话务员满意度" show-overflow-tooltip width="120">
+      <el-table-column label="话务员评价" show-overflow-tooltip width="120">
         <template #default="{ row }">
 						<span v-for="item in row.orderVisit?.orderVisitDetails">
 							<span v-if="item.visitTarget === 10">{{ item.seatEvaluateText }}</span>
 						</span>
         </template>
       </el-table-column>
-<!--      <el-table-column prop="currentStepName" label="部门办件满意度" show-overflow-tooltip width="150"></el-table-column>
-      <el-table-column prop="currentStepName" label="话务员不满意原因" show-overflow-tooltip width="150"></el-table-column>
-      <el-table-column prop="currentStepName" label="办件不满意原因" show-overflow-tooltip width="150"></el-table-column>-->
+      <el-table-column label="部门办件结果" show-overflow-tooltip width="170">
+        <template #default="{ row }">
+						<span v-for="item in row.orderVisit?.orderVisitDetails">
+							<span v-if="item.visitTarget === 20">{{ item.orgProcessingResults.value }}</span>
+						</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="部门办件态度" show-overflow-tooltip width="170">
+        <template #default="{ row }">
+						<span v-for="item in row.orderVisit?.orderVisitDetails">
+							<span v-if="item.visitTarget === 20">{{ item.orgHandledAttitude.value }}</span>
+						</span>
+        </template>
+      </el-table-column>
 		</el-table>
 		<!-- 分页 -->
 		<pagination :total="state.total" v-model:page="state.queryParams.PageIndex" v-model:limit="state.queryParams.PageSize" @pagination="queryList" />

+ 124 - 0
src/views/statistics/call/hotNumber.vue

@@ -0,0 +1,124 @@
+<template>
+	<div class="statistics-call-hot-number-container layout-pd">
+		<!-- 搜索  -->
+		<el-card shadow="never">
+			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
+				<el-form-item label="热线号码" prop="gateway">
+					<el-input v-model="state.queryParams.gateway" placeholder="热线号码" clearable @keyup.enter="queryList" class="keyword-input" />
+				</el-form-item>
+				<el-form-item label="时间段" prop="crTime">
+					<el-date-picker
+						v-model="state.queryParams.crTime"
+						type="datetimerange"
+						unlink-panels
+						range-separator="至"
+						start-placeholder="开始时间"
+						end-placeholder="结束时间"
+						:shortcuts="shortcuts"
+						@change="queryList"
+						value-format="YYYY-MM-DD[T]HH:mm:ss"
+					/>
+				</el-form-item>
+				<el-form-item label-width="0">
+					<el-button type="primary" @click="queryList" :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-form-item>
+			</el-form>
+		</el-card>
+		<el-card shadow="never">
+			<!-- 表格 -->
+			<el-table :data="state.tableData" v-loading="state.loading" ref="multipleTableRef" @selection-change="handleSelectionChange" show-summary>
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="热线号码" show-overflow-tooltip align="center">
+                <template #default="scope"> {{ ''+scope.row.gateWay }} </template>
+        </el-table-column>
+				<el-table-column label="电话呼入汇总" align="center">
+					<el-table-column prop="callInCount" label="呼入" show-overflow-tooltip align="center" sortable></el-table-column>
+					<el-table-column prop="connectCount" label="接通" show-overflow-tooltip align="center" sortable></el-table-column>
+					<el-table-column prop="noConnectByeCount" label="未接通秒挂" show-overflow-tooltip align="center" sortable></el-table-column>
+					<el-table-column label="接通率" show-overflow-tooltip align="center" sortable>
+						<template #default="scope"> {{ scope.row.callInConnectRate }}% </template>
+					</el-table-column>
+				</el-table-column>
+        <el-table-column label="接通话务分析" align="center">
+          <el-table-column prop="effectiveCount" label="有效接通" show-overflow-tooltip align="center" sortable></el-table-column>
+          <el-table-column prop="aveDuration" label="平均时长" show-overflow-tooltip align="center" sortable></el-table-column>
+          <el-table-column prop="durationSum" label="通话时总长" show-overflow-tooltip align="center" sortable></el-table-column>
+          <el-table-column prop="connectByeCount" label="接通秒挂" show-overflow-tooltip align="center" sortable></el-table-column>
+          <el-table-column prop="timelyAnswerCount" label="及时应答数" show-overflow-tooltip align="center" sortable></el-table-column>
+          <el-table-column label="有效率" show-overflow-tooltip align="center" sortable>
+            <template #default="scope"> {{ scope.row.effectiveRate }}% </template>
+          </el-table-column>
+        </el-table-column>
+				<template #empty>
+					<Empty />
+				</template>
+			</el-table>
+		</el-card>
+	</div>
+</template>
+<script setup lang="ts" name="statisticsCallHotNumber">
+import { onMounted, reactive, ref } from 'vue';
+import { ElButton, FormInstance } from 'element-plus';
+import { throttle } from '@/utils/tools';
+import { callHotline } from '@/api/statistics/call';
+import { shortcuts } from '@/utils/constants';
+
+import dayjs from 'dayjs';
+// 定义变量内容
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive(<any>{
+	queryParams: {
+		// 查询条件
+		gateway: null, // 热线号码
+		crTime: [dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')],
+	},
+	tableData: [], //表单
+	loading: false, // 加载
+	total: 0, // 总数
+});
+
+/** 获取列表 */
+const queryList = throttle(() => {
+	state.loading = true;
+	let beginDate = null;
+	let endDate = null;
+	if (state.queryParams?.crTime) {
+		beginDate = state.queryParams?.crTime[0];
+		endDate = state.queryParams?.crTime[1];
+	}
+	const request = {
+		beginDate,
+		endDate,
+		gateway: state.queryParams.gateway,
+		Keyword: state.queryParams.Keyword,
+	};
+	callHotline(request)
+		.then((res: any) => {
+			state.tableData = res.result ?? [];
+			state.total = res.result?.total ?? 0;
+			state.loading = false;
+		})
+		.catch(() => {
+			state.loading = false;
+		});
+}, 300);
+/** 重置按钮操作 */
+const resetQuery = throttle((formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	queryList();
+}, 300);
+// 表格多选
+const multipleTableRef = ref<RefType>();
+const multipleSelection = ref<any>([]);
+const handleSelectionChange = (val: any[]) => {
+	multipleSelection.value = val;
+};
+
+onMounted(() => {
+	queryList();
+});
+</script>

+ 1 - 1
src/views/statistics/call/talkTime.vue

@@ -36,7 +36,7 @@
         border
       >
         <el-table-column type="selection" width="55" />
-        <el-table-column prop="hour" label="时间段" show-overflow-tooltip align="center"> </el-table-column>
+        <el-table-column prop="hourTo" label="时间段" show-overflow-tooltip align="center"> </el-table-column>
         <el-table-column prop="count" label="呼入总量" show-overflow-tooltip align="center"></el-table-column>
         <el-table-column prop="effectiveCount" label="有效接通" show-overflow-tooltip align="center"></el-table-column>
         <el-table-column prop="connectByeCount" label="接通秒挂" show-overflow-tooltip align="center"></el-table-column>

+ 1 - 1
src/views/statistics/order/acceptTopTen.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="statistics-order-center-container layout-pd">
+	<div class="statistics-order-accept-top-ten-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>

+ 2 - 2
src/views/statistics/order/hotspot.vue → src/views/statistics/order/hotspotCount.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="statistics-order-org-overdue-container layout-pd">
+	<div class="statistics-order-hotspot-count-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
@@ -79,7 +79,7 @@
 		</el-card>
 	</div>
 </template>
-<script setup lang="ts" name="statisticsOrderHotspot">
+<script setup lang="ts" name="statisticsOrderHotspotCount">
 import { computed, onMounted, reactive, ref } from 'vue';
 import { ElButton, FormInstance } from 'element-plus';
 import { throttle } from '@/utils/tools';

+ 131 - 0
src/views/statistics/order/hotspotSubclass.vue

@@ -0,0 +1,131 @@
+<template>
+	<div class="statistics-order-hotspot-subclass-container layout-pd">
+		<!-- 搜索  -->
+		<el-card shadow="never">
+			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
+        <el-form-item prop="TypeId" label="类型">
+          <el-select v-model="state.queryParams.TypeId" placeholder="选择类型" @change="queryList">
+            <el-option label="全部" value="0" />
+            <el-option label="市民" value="1" />
+            <el-option label="企业" value="2" />
+          </el-select>
+        </el-form-item>
+				<el-form-item prop="crTime" label="时间段">
+					<el-date-picker
+						v-model="state.queryParams.crTime"
+						type="datetimerange"
+						unlink-panels
+						range-separator="至"
+						start-placeholder="开始时间"
+						end-placeholder="结束时间"
+						:shortcuts="shortcuts"
+						@change="queryList"
+						value-format="YYYY-MM-DD[T]HH:mm:ss"
+						:clearable="false"
+					/>
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" @click="queryList" :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-form-item>
+			</el-form>
+		</el-card>
+		<el-card shadow="never">
+			<!-- 表格 -->
+			<el-table
+				:data="state.tableData"
+				v-loading="state.loading"
+				row-key="hotspotCode"
+				lazy
+				:load="load"
+				:tree-props="{ children: 'children', hasChildren: 'hasChild' }"
+				show-summary
+			>
+				<el-table-column prop="hotspotName" label="热点名称" show-overflow-tooltip></el-table-column>
+				<el-table-column prop="sumCount" label="分类统计" show-overflow-tooltip width="120"> </el-table-column>
+				<template #empty>
+					<Empty />
+				</template>
+			</el-table>
+		</el-card>
+	</div>
+</template>
+<script setup lang="ts" name="statisticsOrderHotspotSubclass">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { ElButton, FormInstance } from 'element-plus';
+import { throttle } from '@/utils/tools';
+import { departmentHotSmall } from '@/api/statistics/order';
+import { shortcuts } from '@/utils/constants';
+import dayjs from 'dayjs';
+// 定义变量内容
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive(<any>{
+	queryParams: {
+		// 查询条件
+    TypeId: '0', // 关键词
+		time: dayjs(new Date()).format('YYYY-MM-DD'), // 时间默认今天
+		crTime: [dayjs(new Date()).startOf('date').format('YYYY-MM-DD[T]HH:mm:ss'), dayjs(new Date()).endOf('date').format('YYYY-MM-DD[T]HH:mm:ss')], // 时间默认今天开始到今天结束
+	},
+	tableData: [], //表单
+	loading: false, // 加载
+	total: 0, // 总数
+});
+/** 获取列表 */
+const queryList = throttle(() => {
+	state.loading = true;
+	let StartDate = null;
+	let EndDate = null;
+  if (state.queryParams?.crTime) {
+    StartDate = state.queryParams?.crTime[0];
+    EndDate = state.queryParams?.crTime[1];
+  }
+	const request = {
+    TypeId: state.queryParams.TypeId,
+    StartDate,
+    EndDate,
+	};
+  departmentHotSmall(request)
+		.then((res: any) => {
+			state.tableData = res.result ?? [];
+      state.tableData = state.tableData.map((item: any) => {
+        item.children = []
+        return item;
+      });
+			state.loading = false;
+		})
+		.catch(() => {
+			state.loading = false;
+		});
+}, 300);
+/** 重置按钮操作 */
+const resetQuery = throttle((formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+	queryList();
+}, 300);
+// 懒加载
+const load = (row: any, treeNode: unknown, resolve: (date: any[]) => void) => {
+	let StartDate = null;
+	let EndDate = null;
+  if (state.queryParams?.crTime) {
+    StartDate = state.queryParams?.crTime[0];
+    EndDate = state.queryParams?.crTime[1];
+  }
+	const request = {
+    TypeId: state.queryParams.TypeId,
+    StartDate,
+    EndDate,
+    HotspotCode: row.hotspotCode,
+	};
+  departmentHotSmall(request)
+		.then((res: any) => {
+			resolve(res.result ?? []);
+		})
+		.catch(() => {});
+};
+onMounted(() => {
+	queryList();
+});
+</script>

+ 1 - 1
src/views/statistics/order/orgDelay.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="statistics-order-org-overdue-container layout-pd">
+	<div class="statistics-order-org-delay-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent  inline>

+ 1 - 1
src/views/statistics/order/orgHotspot.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="statistics-order-center-container layout-pd">
+	<div class="statistics-order-hotspot-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>

+ 1 - 1
src/views/statistics/order/specialTable.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="business-visit-container layout-pd">
+	<div class="statistics-order-special-table-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>

+ 1 - 1
src/views/statistics/order/specials.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="statistics-order-org-overdue-container layout-pd">
+	<div class="statistics-order-specials-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent  inline>

+ 104 - 0
src/views/statistics/order/visitCount.vue

@@ -0,0 +1,104 @@
+<template>
+  <div class="statistics-order-visit-count-container layout-pd">
+    <!-- 搜索  -->
+    <el-card shadow="never">
+      <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
+        <el-form-item label="回访员" prop="VisitName">
+          <el-input v-model="state.queryParams.VisitName" placeholder="回访员名称" clearable @keyup.enter="queryList" class="keyword-input" />
+        </el-form-item>
+        <el-form-item label="时间段" prop="crTime">
+          <el-date-picker
+            v-model="state.queryParams.crTime"
+            type="datetimerange"
+            unlink-panels
+            range-separator="至"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            :shortcuts="shortcuts"
+            @change="queryList"
+            value-format="YYYY-MM-DD[T]HH:mm:ss"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="queryList" :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-form-item>
+      </el-form>
+    </el-card>
+    <el-card shadow="never">
+      <!-- 表格 -->
+      <el-table :data="tableData" v-loading="state.loading" ref="multipleTableRef" @selection-change="handleSelectionChange" border show-summary>
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column prop="visitName" label="回访员" show-overflow-tooltip align="center"></el-table-column>
+        <el-table-column prop="aiVisitCount" label="智能回访量" show-overflow-tooltip align="center"></el-table-column>
+        <el-table-column prop="artificialVisitCount" label="人工回访量" show-overflow-tooltip align="center"></el-table-column>
+        <el-table-column prop="sumCount" label="总回访量" show-overflow-tooltip align="center"></el-table-column>
+        <template #empty>
+          <Empty />
+        </template>
+      </el-table>
+    </el-card>
+  </div>
+</template>
+<script setup lang="ts" name="statisticsOrderVisitCount">
+import { onMounted, reactive, ref } from 'vue';
+import { ElButton, FormInstance } from 'element-plus';
+import { throttle } from '@/utils/tools';
+import { departmentVisit } from '@/api/statistics/order';
+import { shortcuts } from '@/utils/constants';
+import dayjs from 'dayjs';
+// 定义变量内容
+
+const columns = ref([]);
+const tableData = ref([]);
+const ruleFormRef = ref<RefType>(); // 表单ref
+const state = reactive(<any>{
+  queryParams: {
+    // 查询条件
+    VisitName: null, // 关键词
+    crTime: [dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')], // 时间默认今天开始到今天结束
+  },
+  loading: false, // 加载
+  total: 0, // 总数
+});
+/** 获取列表 */
+const queryList = throttle(() => {
+  state.loading = true;
+  let StartDate = null;
+  let EndDate = null;
+  if (state.queryParams?.crTime) {
+    StartDate = state.queryParams?.crTime[0];
+    EndDate = state.queryParams?.crTime[1];
+  }
+  const request = {
+    StartDate,
+    EndDate,
+    VisitName: state.queryParams.VisitName,
+  };
+  departmentVisit(request)
+    .then((res: any) => {
+      tableData.value = res.result;
+      state.loading = false;
+    })
+    .catch(() => {
+      state.loading = false;
+    });
+}, 300);
+/** 重置按钮操作 */
+const resetQuery = throttle((formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
+  queryList();
+}, 300);
+// 表格多选
+const multipleTableRef = ref<RefType>();
+const multipleSelection = ref<any>([]);
+const handleSelectionChange = (val: any[]) => {
+  multipleSelection.value = val;
+};
+onMounted(() => {
+  queryList();
+});
+</script>

+ 1 - 1
src/views/statistics/order/visitDiscontent.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="statistics-call-transfer-out-container layout-pd">
+	<div class="statistics-order-visit-discontent-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>

+ 1 - 1
src/views/statistics/order/visitTable.vue

@@ -1,5 +1,5 @@
 <template>
-	<div class="business-visit-container layout-pd">
+	<div class="business-visit-table-container layout-pd">
 		<!-- 搜索  -->
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>

+ 1 - 1
src/views/system/menu/index.vue

@@ -91,7 +91,7 @@ const state = reactive<any>({
 			width: 300,
 			cellRenderer: (data: any) => {
 				return (
-					<p style="dispaly:flex;align-items:center">
+					<p style="display:flex;align-items:center">
 						<MenuSvgIcon name={data.rowData.icon} />
 						<span class="pl5">{data.rowData.pageName}</span>
 					</p>

+ 155 - 0
src/views/todo/countersign/index.vue

@@ -0,0 +1,155 @@
+<template>
+  <div class="todo-countersign-container layout-pd">
+    <!-- 搜索  -->
+    <el-card shadow="never">
+      <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent  inline>
+        <el-form-item label="关键词" prop="Keyword">
+          <el-input v-model="state.queryParams.Keyword" placeholder="工单编码/标题" clearable @keyup.enter="queryList" class="keyword-input"/>
+        </el-form-item>
+        <el-form-item label="是否省工单" prop="IsProvince">
+          <el-select v-model="state.queryParams.IsProvince" placeholder="请选择是否省工单" class="w100">
+            <el-option label="是" value="true" />
+            <el-option label="否" value="false" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="queryList" :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-form-item>
+      </el-form>
+    </el-card>
+    <el-card shadow="never">
+      <!-- 表格 -->
+      <el-table :data="state.tableData" v-loading="state.loading">
+        <el-table-column label="超期状态" width="80" align="center">
+          <template #default="{ row }">
+            <span :class="'overdue-status-'+row.order?.expiredStatus" :title="row.order?.expiredStatusText"></span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="id" label="会签编码" show-overflow-tooltip width="300"></el-table-column>
+        <el-table-column prop="order.no" label="工单编码" show-overflow-tooltip width="150"></el-table-column>
+        <el-table-column prop="order.sourceChannel" label="来源方式" show-overflow-tooltip></el-table-column>
+        <el-table-column width="100" label="省/市工单" prop="isProvince">
+          <template #default="{ row }">
+            <span>{{ row.order?.isProvince ? '省工单' : '市工单' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="工单标题" prop="no" show-overflow-tooltip width="300">
+          <template #default="{ row }">
+            <order-detail :order="row.order" @updateList="queryList">{{ row.order?.title }}</order-detail>
+          </template>
+        </el-table-column>
+        <el-table-column label="工单期满时间" show-overflow-tooltip width="170">
+          <template #default="{ row }">
+            <span>{{ formatDate(row.order?.expiredTime, 'YYYY-mm-dd HH:MM:SS') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="startTime" label="受理时间" show-overflow-tooltip width="170">
+          <template #default="{ row }">
+            <span>{{ formatDate(row.order?.startTime, 'YYYY-mm-dd HH:MM:SS') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="order.acceptType" label="受理类型" show-overflow-tooltip width="150"></el-table-column>
+        <el-table-column label="工单状态" show-overflow-tooltip width="100" prop="order.statusText"></el-table-column>
+        <el-table-column prop="order.actualHandleOrgName" label="接办部门" show-overflow-tooltip width="170"></el-table-column>
+        <el-table-column label="接办时间" show-overflow-tooltip width="170">
+          <template #default="{ row }">
+            <span>{{ formatDate(row.order?.actualHandleTime, 'YYYY-mm-dd HH:MM:SS') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="order.hotspotName" label="热点分类" show-overflow-tooltip width="150"></el-table-column>
+        <el-table-column prop="starterOrgName" label="会签发起部门" show-overflow-tooltip width="150"></el-table-column>
+        <el-table-column prop="starterName" label="会签发起人" show-overflow-tooltip width="120"></el-table-column>
+        <el-table-column label="会签发起时间" show-overflow-tooltip width="170">
+          <template #default="{ row }">
+            <span>{{ formatDate(row.creationTime, 'YYYY-mm-dd HH:MM:SS') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="100" fixed="right" align="center">
+          <template #default="{ row }">
+            <!--						<el-button link type="primary" @click="onCountersignDetail(row)" title="查看会签明细" v-auth="'query:countersign:detail'">
+              会签明细
+            </el-button>-->
+            <order-detail :order="row.order" @updateList="queryList" />
+          </template>
+        </el-table-column>
+        <template #empty>
+          <Empty />
+        </template>
+      </el-table>
+      <!-- 分页 -->
+      <pagination
+        :total="state.total"
+        v-model:page="state.queryParams.PageIndex"
+        v-model:limit="state.queryParams.PageSize"
+        @pagination="queryList"
+      />
+    </el-card>
+    <!-- 会签明细 -->
+    <countersign-detail ref="countersignDetailRef" @updateList="queryList" />
+  </div>
+</template>
+<script setup lang="ts" name="todoCountersign">
+import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
+import { ElButton, ElMessage, FormInstance } from 'element-plus';
+import { auth } from '@/utils/authFunction';
+import { throttle } from '@/utils/tools';
+import { formatDate } from '@/utils/formatTime';
+import { useRouter } from 'vue-router';
+import { countersignList } from '@/api/query/countersign';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '@/stores/userInfo';
+// 引入组件
+const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
+const CountersignDetail = defineAsyncComponent(() => import('@/views/query/countersign/detail/index.vue')); // 会签明细
+// 定义变量内容
+const ruleFormRef = ref<RefType>(); // 表单ref
+const router = useRouter(); // 路由
+const state = reactive(<any>{
+  queryParams: {
+    // 查询条件
+    PageIndex: 1,
+    PageSize: 10,
+    Keyword: null, // 关键字
+  },
+  tableData: [], //表单
+  loading: false, // 加载
+  total: 0, // 总数
+});
+const storesUserInfo = useUserInfo();
+const { userInfos } = storeToRefs(storesUserInfo); // 用户信息
+/** 获取列表 */
+const queryList = throttle(() => {
+  if (!auth('query:Countersign:query')) ElMessage.error('抱歉,您没有权限查看会签查询!');
+  else {
+    state.loading = true;
+    state.queryParams.IsOnlyStarter = userInfos.value.isCenter;
+    countersignList(state.queryParams)
+      .then((res) => {
+        state.tableData = res.result?.items ?? [];
+        state.total = res.result?.totalCount ?? 0;
+      })
+      .catch(() => {})
+      .finally(() => {
+        state.loading = false;
+      });
+  }
+}, 300);
+
+/** 重置按钮操作 */
+const resetQuery = throttle((formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
+  queryList();
+}, 300);
+// 会签明细
+const countersignDetailRef = ref<RefType>(); // 会签明细ref
+const onCountersignDetail = (row: any) => {
+  countersignDetailRef.value.openDialog(row);
+};
+onMounted(() => {
+  queryList();
+});
+</script>

+ 1 - 1
src/views/todo/seats/accept/Expand-form.vue

@@ -633,7 +633,7 @@
 						</el-text>
 					</el-divider>
 					<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6">
-						<el-form-item label="商品分类/品牌" prop="brandCode" :rules="[{ required: true, message: '请选择商品分类/品牌', trigger: 'change' }]">
+						<el-form-item label="商品分类/品牌" prop="brandCode" :rules="[{ required: false, message: '请选择商品分类/品牌', trigger: 'change' }]">
 							<el-cascader
 								:options="state.brandOptions"
 								filterable

+ 17 - 29
src/views/todo/seats/accept/Voice-assistant.vue

@@ -105,11 +105,11 @@
 import { nextTick, onActivated, onDeactivated, onMounted, ref, watch } from 'vue';
 import { ElMessageBox } from 'element-plus';
 import { getImageUrl } from '@/utils/tools';
-import axios from 'axios';
 import { useRoute } from 'vue-router';
 import { formatDate } from '@/utils/formatTime';
 const emit = defineEmits(['orderOverwrite']);
 import mittBus from '@/utils/mitt';
+import { voiceAssistantContent } from "@/api/todo/voiceAssistant";
 // 消息列表
 const messageList = ref<any>([
 	/*{
@@ -172,11 +172,8 @@ const wsReceive = (message: any) => {
 				setTimeout(() => {
 					//通话结束
 					talkEnd.value = true;
-					getRecognize();
-					scrollToBottom();
-					setTimeout(() => {
-						stopScroll();
-					}, 500);
+					scrollToBottom(); //滚动到底部
+          stopScroll(); //停止滚动
 				}, 1000);
 				console.log('通话转写结束了');
 			}
@@ -188,18 +185,24 @@ const wsReceive = (message: any) => {
 const scrollbarRef = ref<RefType>();
 const chatBoxRef = ref<RefType>();
 const searchCol = ref(true); // 展开/收起
-// 展开/收起
+// 展开/收起 通话小结
 const closeSearch = () => {
+  if (!searchCol.value) {
+    // 展开
+    // 获取识别内容
+    getRecognize();
+  }
 	searchCol.value = !searchCol.value;
 };
 // 获取识别内容
-const getRecognize = () => {
-	const callId = route.params.callId;
-	axios.get(`${import.meta.env.VITE_VOICE_ASSISTANT_API_URL}/monitor/remote/details/${callId}`).then((res) => {
-		const { data } = res;
-		const { result } = data;
-		recognizeList.value = result;
-	});
+const getRecognize = async () => {
+  try {
+    const callId = route.params.callId;
+    const {result} = await voiceAssistantContent(typeof callId === "string" ? callId :'');
+    recognizeList.value = result;
+  }catch (error) {
+    console.log('获取识别内容失败', error);
+  }
 };
 // 停止滚动
 const stopScroll = () => {
@@ -239,21 +242,6 @@ const fillSingle = () => {
 		})
 		.catch(() => {});
 };
-/*setInterval(() => {
-	messageList.value.push({
-		body: {
-			content: {
-				callSentenceInfo: {
-					text: '你好,我是小智,有什么可以帮您的吗?',
-					role: 'agent',
-				},
-				calledNumber: '1009',
-				callerNumber: '19136073037',
-			},
-		},
-		timestamps: new Date().getTime(),
-	});
-}, 1000);*/
 watch(
 	() => scrollbarRef.value?.wrapRef,
 	() => {

+ 9 - 6
src/views/todo/seats/accept/index.vue

@@ -89,7 +89,7 @@
 									<!-- 来电弹单 -->
 									<template v-if="state.createBy === 'tel'">
 										<el-form-item label="来电号码" prop="fromPhone">
-											<el-input v-model="state.ruleForm.fromPhone" placeholder="请填写来电号码"> </el-input>
+											<el-input v-model="state.ruleForm.fromPhone" placeholder="请填写来电号码" disabled> </el-input>
 										</el-form-item>
 									</template>
 									<!-- 互联网来信 -->
@@ -369,6 +369,7 @@
 													ref="areaRef"
 													@change="changeArea"
 												>
+
 												</el-cascader>
 											</el-form-item>
 										</el-col>
@@ -508,7 +509,7 @@
 </template>
 
 <script setup lang="ts" name="orderAccept">
-import { computed, defineAsyncComponent, onBeforeMount, onMounted, reactive, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, nextTick, onBeforeMount, onMounted, reactive, ref, watch } from "vue";
 import type { FormInstance } from 'element-plus';
 import { ElMessage, ElMessageBox, ElNotification } from 'element-plus';
 import { storeToRefs } from 'pinia';
@@ -570,7 +571,7 @@ const state = reactive<any>({
 		eventCategoryId: '', // 事件分类
 		incidentTime: null, // 事发时间
 		incidentPurpose: null, // 事件目的
-		areaCode: null, // 区域编码
+		areaCode: '511500', // 区域编码
 		street: null, // 街道
 		isRepeat: 'false', // 是否重复工单 默认否
 		pushType: null, // 推送类型
@@ -1030,9 +1031,10 @@ const loadForm = async () => {
 			//通话
 			state.ruleForm.sourceChannel = '电话'; //电话
 			state.ruleForm.sourceChannelCode = 'RGDH'; //电话
-			state.ruleForm.fromPhone = historyParams.fromTel;
-			state.ruleForm.callId = historyParams.telGuid;
-			state.ruleForm.transferPhone = historyParams.transfer;
+			state.ruleForm.fromPhone = historyParams.fromTel; // 来电号码
+      state.ruleForm.contact = historyParams.fromTel; // 联系电话
+			state.ruleForm.callId = historyParams.telGuid; // 通话id
+			state.ruleForm.transferPhone = historyParams.transfer; // 转接来源
 			state.ruleForm.callAddress = historyParams.telArea; // 来电归属地
 			state.ruleForm.channel = {
 				// 来源渠道
@@ -1132,5 +1134,6 @@ onBeforeMount(() => {
 onMounted(async () => {
 	await loadAddress();
 	await loadExtra();
+  await nextTick()
 });
 </script>

+ 12 - 0
src/views/todo/visit/index.vue

@@ -6,6 +6,18 @@
 				<el-form-item label="关键字" prop="Keyword">
 					<el-input v-model="state.queryParams.Keyword" placeholder="工单编码/标题" clearable @keyup.enter="queryList" class="keyword-input" />
 				</el-form-item>
+        <el-form-item label="归档类型" prop="FiledType">
+          <el-select v-model="state.queryParams.FiledType" placeholder="请选择归档类型">
+            <el-option label="中心归档" value="10" />
+            <el-option label="部门归档" value="20" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="是否会签" prop="IsCountersign">
+          <el-select v-model="state.queryParams.IsCountersign" placeholder="请选择是否会签">
+            <el-option label="是" value="true" />
+            <el-option label="否" value="false" />
+          </el-select>
+        </el-form-item>
 				<el-form-item>
 					<el-button type="primary" @click="queryList" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
 					<el-button @click="resetQuery(ruleFormRef)" v-waves class="default-button" :loading="state.loading">