Эх сурвалжийг харах

Merge branch 'release' of http://git.12345lm.cn/Fengwo/hotline-web into release

xf 8 сар өмнө
parent
commit
a1c735bd29

+ 2 - 2
.env.development

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

+ 26 - 0
src/api/business/visit.ts

@@ -56,6 +56,17 @@ export const visitSearchBaseData = () => {
         method: 'get'
     });
 }
+/**
+ * @description 回访扭转列表基础数据
+ * @param {object} params
+ */
+export const visitTurnBaseData = (params?: object) => {
+    return request({
+        url: `/api/v1/Order/visit/judge-query-basedata`,
+        method: 'get',
+        params
+    });
+};
 /**
  * @description 回访扭转列表
  * @param {object} params
@@ -67,6 +78,21 @@ export const visitTurnList = (params: object) => {
         params,
     });
 };
+/**
+ * @description 回访扭转列表导出
+ * @param {object} data
+ */
+export const visitTurnExport = (data: object) => {
+  return request({
+
+    url: `/api/v1/Order/visit/judge-query-export`,
+    method: 'post',
+    data,
+    responseType: 'blob',
+  }, {
+    reduce_data_format: false
+  });
+}
 /**
  * @description 扭转满意度
  * @param {object} data

+ 25 - 0
src/api/statistics/center.ts

@@ -569,4 +569,29 @@ export const centerAcceptDutyTimeExport = (data: object) => {
   }, {
     reduce_data_format: false
   });
+}
+/**
+ * @description 扭转信件统计
+ * @param {object} params
+ */
+export const centerTurn = (params: object) => {
+  return request({
+    url: `/api/v1/BiOrder/ordervisit-jude-statistics`,
+    method: 'get',
+    params,
+  });
+}
+/**
+ * @description 扭转信件统计导出
+ * @param {object} data
+ */
+export const centerTurnExport = (data: object) => {
+  return request({
+    url: `/api/v1/BiOrder/ordervisit-jude-statistics-export`,
+    method: 'post',
+    data,
+    responseType: 'blob',
+  }, {
+    reduce_data_format: false
+  });
 }

+ 37 - 7
src/components/AudioPlayer/index.vue

@@ -51,13 +51,26 @@
 						<SvgIcon :name="icon" class="mr5 ml5 cursor-pointer" size="18px" color="var(--el-color-primary)" @click="mute" />
 						<template #dropdown>
 							<el-dropdown-menu>
-								<el-slider v-model="audioData.voice" vertical height="100px" @change="changeSound" />
+								<el-slider v-model="audioData.voice" vertical height="100px" @change="changeSound" style="padding:10px 0 "/>
 							</el-dropdown-menu>
 						</template>
 					</el-dropdown>
-					<el-button link :loading="loading" v-auth="'public:record:download'">
-						<SvgIcon name="ele-Download" class="cursor-pointer ml5" size="18px" color="var(--el-color-primary)" @click="downLoad" title="下载录音" />
-					</el-button>
+					<el-dropdown @command="changeSpeed" trigger="hover">
+						<el-button type="primary" link>{{speed}}倍速</el-button>
+									<template #dropdown>
+										<el-dropdown-menu>
+											<el-dropdown-item command="0.5">0.5</el-dropdown-item>
+											<el-dropdown-item command="1">1</el-dropdown-item>
+											<el-dropdown-item command="1.5">1.5</el-dropdown-item>
+											<el-dropdown-item command="2">2</el-dropdown-item>
+											<el-dropdown-item command="2.5">2.5</el-dropdown-item>
+											<el-dropdown-item command="3">3</el-dropdown-item>
+										</el-dropdown-menu>
+									</template>
+								</el-dropdown>
+<!--					<el-button link :loading="loading" v-auth="'public:record:download'">
+						<SvgIcon name="ele-Download" class="cursor-pointer ml5" size="18px" color="var(&#45;&#45;el-color-primary)" @click="downLoad" title="下载录音" />
+					</el-button>-->
 				</div>
 			</div>
 		</div>
@@ -108,7 +121,7 @@ const audioData = reactive<any>({
 	duration: 0, // 音频总时长
 	currentTime: 0, // 当前播放的时间
 	percentage: 0, //进度条百分比
-	voice: 50, //音量
+	voice: 100, //音量
 	muteState: false, //是否静音
 });
 
@@ -228,8 +241,8 @@ watch(
 		if (val) {
 			audioRef.value.volume = audioData.voice = 0;
 		} else {
-			audioRef.value.volume = 0.5;
-			audioData.voice = 50;
+			audioRef.value.volume = 1;
+			audioData.voice = 100;
 		}
 	},
 	{
@@ -240,6 +253,12 @@ watch(
 const mute = () => {
 	audioData.muteState = !audioData.muteState;
 };
+// 改变倍速
+const speed = ref(1);
+const changeSpeed = (val: number) => {
+	audioRef.value.playbackRate = val;
+	speed.value = val;
+};
 const loading = ref(false);
 const storesThemeConfig = useThemeConfig();
 const { themeConfig } = storeToRefs(storesThemeConfig);
@@ -273,6 +292,17 @@ nextTick(() => {
 		ElMessage.error('音频加载失败');
 	};
 });
+defineExpose({
+	playPauseAudio,
+	handleBack,
+	handleForward,
+	inputProgress,
+	changeProgress,
+	formatTooltip,
+	changeSound,
+	mute,
+	downLoad
+})
 </script>
 <style scoped lang="scss">
 .gloWe-audio {

+ 168 - 0
src/components/PlayRecord/index.vue

@@ -0,0 +1,168 @@
+<template>
+	<el-dialog
+		v-model="dialogVisible"
+		width="600px"
+		draggable
+		title="播放录音"
+		append-to-body
+		destroy-on-close
+		:modal="false"
+		modal-class="modal_class"
+		class="dialog_class_record"
+		:close-on-click-modal="false"
+
+	>
+		<div v-loading="loading">
+			<el-table :data="tableData" border>
+				<el-table-column prop="name" label="来电号码" align="center"/>
+				<el-table-column prop="createTime" label="来电时间" width="160" align="center"/>
+				<el-table-column prop="duration" label="时长" align="center"/>
+				<el-table-column prop="size" label="操作" align="center">
+					<template #default="{ row }">
+						<el-button type="primary" link v-auth="'public:record:download'" @click="download(row)">下载</el-button>
+					</template>
+				</el-table-column>
+			</el-table>
+			<audio-player :url="RecordUrl" :fileName="fileName" :recordingAbsolutePath="recordingAbsolutePath" :key="RecordUrl"/>
+		</div>
+	</el-dialog>
+</template>
+
+<script lang="ts" setup name="playRecord">
+import { defineAsyncComponent, ref } from 'vue';
+import { fileDownload } from '@/api/public/file';
+import { downloadFileByStream } from '@/utils/tools';
+// 引入组件
+const AudioPlayer = defineAsyncComponent(() => import('@/components/AudioPlayer/index.vue'));
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['openDialog', 'closeDialog']);
+const RecordUrl = ref<string>(''); //录音地址
+const fileName = ref<string>(''); // 文件名
+const recordingAbsolutePath = ref<string>(''); // 录音绝对路径
+const fromPhone = ref<string>(''); // 来电号码
+const createTime = ref<string>(''); // 创建时间
+const dialogVisible = ref<boolean>(false); //是否显示弹窗
+const tableData = ref<EmptyArrayType>([]);
+const loading = ref(false);
+// 打开弹窗
+const openDialog = async (url: string, recordingFileName, recordingPath, phone, time) => {
+	loading.value = true;
+	tableData.value = [
+		{
+			name: '18683069157',
+			createTime: ' 2024-08-29 16:35:05',
+			duration: '392秒',
+		}
+	]
+	RecordUrl.value = url;
+	fileName.value = recordingFileName;
+	recordingAbsolutePath.value = recordingPath;
+	fromPhone.value = phone;
+	createTime.value = time;
+	dialogVisible.value = true;
+	setTimeout(()=>{
+		loading.value = false;
+	},300)
+	emit('openDialog');
+
+	/*let popup = window.open('', '_blank', 'left=100,top=100,width=400,height=200');
+	if (popup) {
+		popup.document.write(`
+        <html>
+          <head>
+            <title>播放录音</title>
+            <style>
+              body,html {
+               margin: 0;
+              padding: 0;
+              width: 100%;
+              height: 100%;
+              font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
+              font-weight: 400;
+              -webkit-font-smoothing: antialiased;
+              -webkit-tap-highlight-color: transparent;
+              background-color: var(--hotline-bg-main-color);
+              font-size: 14px;
+              overflow: hidden;
+              position: relative;
+              }
+              .play-content{
+                height: calc(100% - 40px);
+                padding:20px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                flex-direction: column;
+              }
+              h2{
+                margin:0;
+                padding:0;
+              }
+              .audio-player {
+                   margin-top: 20px;
+              }
+            </style>
+          </head>
+          <body>
+             <div class="play-content">
+               <h2>播放录音</h2>
+                <audio controls class="audio-player">
+                  <source src="${url}" type="audio/wav">
+                  您的浏览器不支持音频组件
+                </audio>
+              </div>
+          </body>
+        </html>
+      `);
+	} else {
+		alert('浏览器阻止了弹出窗口,请允许弹出窗口后再试。');
+	}*/
+};
+// 关闭弹窗
+const closeDialog = () => {
+	dialogVisible.value = false;
+	emit('closeDialog');
+};
+// 下载录音
+const download = (row:any)=>{
+	console.log(row)
+	loading.value = true;
+	// 无绝对路径直接下载
+/*	fileDownload({ path: props.url })
+		.then((res: any) => {
+			downloadFileByStream(res, <string>props.fileName);
+			loading.value = false;
+		})
+		.catch(() => {
+			loading.value = false;
+		});*/
+}
+//暴漏变量和方法
+defineExpose({ closeDialog, openDialog });
+</script>
+<style lang="scss" scoped>
+.item {
+	display: flex;
+	align-items: center;
+	span {
+		display: inline-block;
+		width: 60px;
+		margin-right: 10px;
+	}
+	b {
+		flex: 1;
+		font-weight: normal;
+	}
+}
+</style>
+<style lang="scss">
+.dialog_class_record {
+	pointer-events: auto;
+	flex-direction: column;
+	margin: 0 !important;
+	position: absolute;
+	top: 30vh;
+	left:calc(50vw - 300px);
+}
+</style>
+

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

@@ -34,10 +34,12 @@ const Horizontal = defineAsyncComponent(() => import('@/layout/navMenu/horizonta
 
 const YiBin = defineAsyncComponent(() => import('@/layout/navBars/breadcrumb/ybTel.vue')); // 宜宾呼叫中心(天润)
 const ZiGong = defineAsyncComponent(() => import('@/layout/navBars/breadcrumb/zgTel.vue')); // 自贡呼叫中心(兴唐)
+const LuZhou = defineAsyncComponent(() => import('@/layout/navBars/breadcrumb/zgTel.vue')); // 泸州呼叫中心(兴唐)
 
 const COMPONENT_LIST = {
 	YiBin,
 	ZiGong,
+	LuZhou
 };
 // 定义变量内容
 const stores = useRoutesList();

+ 1 - 1
src/theme/element.scss

@@ -235,7 +235,7 @@
 	border-radius: var(--el-border-radius-base);
 	padding: 0;
 	.el-dialog__title {
-		font-size: 18px;
+		font-size: var(--el-font-size-large);
 		font-weight: bold;
 	}
 	.el-dialog__header {

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

@@ -14,7 +14,7 @@
         <template #table-search>
           <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="handleQuery" class="keyword-input" />
+              <el-input v-model="state.queryParams.Keyword" placeholder="工单标题" clearable @keyup.enter="handleQuery" class="keyword-input" />
             </el-form-item>
             <el-form-item label="工单编码" prop="No">
               <el-input v-model="state.queryParams.No" placeholder="工单编码" clearable @keyup.enter="handleQuery" class="keyword-input" />

+ 22 - 16
src/views/business/visit/reverse.vue

@@ -1,7 +1,7 @@
 <template>
 	<div class="business-visit-reverse-container layout-padding">
     <div class="layout-padding-auto layout-padding-view pd20">
-			<el-tabs v-model="state.queryParams.IsIng" @tab-change="handleQuery">
+			<el-tabs v-model="state.queryParams.IsIngString" @tab-change="handleQuery">
 				<el-tab-pane name="true" label="扭转待评判"></el-tab-pane>
 				<el-tab-pane name="false" label="扭转已评判"></el-tab-pane>
 			</el-tabs>
@@ -14,6 +14,9 @@
 				:total="state.total"
 				v-model:page-index="state.queryParams.PageIndex"
 				v-model:page-size="state.queryParams.PageSize"
+				:toolButton="['refresh', 'setting', 'exportCurrent', 'exportAll']"
+				:exportMethod="visitTurnExport"
+				:exportParams="requestParams"
 			>
         <template #table-search>
           <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent label-width="100px">
@@ -141,7 +144,7 @@
                   </el-form-item>
                 </el-col>
               </transition>
-              <transition name="el-zoom-in-top" v-show="!searchCol && state.queryParams.IsIng === 'false'">
+              <transition name="el-zoom-in-top" v-show="!searchCol && state.queryParams.IsIngString === 'false'">
                 <el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
                   <el-form-item label="评判状态" prop="JudgeState">
                     <el-select v-model="state.queryParams.JudgeState" placeholder="请选择评判状态" clearable class="w100" @change="handleQuery">
@@ -195,7 +198,7 @@
 						:disabled="!scope.isSelected"
 						:loading="state.loading"
 						v-auth="'business:visit:reverse:multiple'"
-						v-if="state.queryParams.IsIng === 'true'"
+						v-if="state.queryParams.IsIngString === 'true'"
 						>批量扭转评判
 					</el-button>
 				</template>
@@ -233,7 +236,7 @@ import { FormInstance } from 'element-plus';
 import { formatDate } from '@/utils/formatTime';
 import { defaultTimeStartEnd, shortcuts } from "@/utils/constants";
 import other from '@/utils/other';
-import { visitTurnList } from '@/api/business/visit';
+import { visitTurnExport, visitTurnList } from '@/api/business/visit';
 import { listBaseData } from '@/api/judicial';
 // 引入组件
 const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
@@ -282,7 +285,8 @@ const state = reactive<any>({
 		// 查询条件
 		PageIndex: 1,
 		PageSize: 20,
-		IsIng: 'true',
+		IsIngString: 'true',
+		IsIng:true,
 		No: null,
 		Title: null,
 		VisitUserName: null,
@@ -331,19 +335,21 @@ const handleQuery = () => {
 	queryList();
 };
 /** 获取列表 */
+const requestParams = ref<EmptyObjectType>({})
 const queryList = () => {
-	let request = other.deepClone(state.queryParams);
-	request.CreationTimeStart = state.queryParams.slTime === null ? null : state.queryParams.slTime[0];
-	request.CreationTimeEnd = state.queryParams.slTime === null ? null : state.queryParams.slTime[1];
-	Reflect.deleteProperty(request, 'slTime'); // 受理时间
-	request.ActualHandleTimeStart = state.queryParams.bjTime === null ? null : state.queryParams.bjTime[0];
-	request.ActualHandleTimeEnd = state.queryParams.bjTime === null ? null : state.queryParams.bjTime[1];
-	Reflect.deleteProperty(request, 'bjTime'); // 办结时间
-	request.VisitTimeStart = state.queryParams.hfTime === null ? null : state.queryParams.hfTime[0];
-	request.VisitTimeEnd = state.queryParams.hfTime === null ? null : state.queryParams.hfTime[1];
-	Reflect.deleteProperty(request, 'hfTime'); // 回访时间
+	state.queryParams.IsIng = state.queryParams.IsIngString === 'true';
+	requestParams.value = other.deepClone(state.queryParams);
+	requestParams.value.CreationTimeStart = state.queryParams.slTime === null ? null : state.queryParams.slTime[0];
+	requestParams.value.CreationTimeEnd = state.queryParams.slTime === null ? null : state.queryParams.slTime[1];
+	Reflect.deleteProperty(requestParams.value, 'slTime'); // 受理时间
+	requestParams.value.ActualHandleTimeStart = state.queryParams.bjTime === null ? null : state.queryParams.bjTime[0];
+	requestParams.value.ActualHandleTimeEnd = state.queryParams.bjTime === null ? null : state.queryParams.bjTime[1];
+	Reflect.deleteProperty(requestParams.value, 'bjTime'); // 办结时间
+	requestParams.value.VisitTimeStart = state.queryParams.hfTime === null ? null : state.queryParams.hfTime[0];
+	requestParams.value.VisitTimeEnd = state.queryParams.hfTime === null ? null : state.queryParams.hfTime[1];
+	Reflect.deleteProperty(requestParams.value, 'hfTime'); // 回访时间
 	state.loading = true;
-	visitTurnList(request)
+	visitTurnList(requestParams.value)
 		.then((response: any) => {
 			state.tableData = response?.result.items ?? [];
 			state.total = response?.result.total;

+ 4 - 11
src/views/dataShare/otherPlatform.vue

@@ -51,18 +51,11 @@ const proTableRef = ref<RefType>(); // 表格ref
 // 表格配置项
 const columns = ref<any[]>([
 	{ prop: 'orderNo', label: '工单编码', align: 'center', width: 140 },
-	{
-		prop: 'creationTime',
-		label: '创建时间',
-		align: 'center',
-		width: 160,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.creationTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
-		},
-	},
+	{ prop: 'source', label: '业务平台', align: 'center',width: 120 },
 	{ prop: 'platformsName', label: '平台名称', align: 'center',width: 120 },
-	{ prop: 'rawData', label: '数据', minWidth: 200, align: 'center' },
-	{ prop: 'serviceInterface', label: 'serviceInterface', align: 'center' }
+	{ prop: 'receiveData ', label: '原始数据', minWidth: 200, align: 'center' },
+	{ prop: 'operationType', label: '操作', align: 'center' },
+	{ prop: 'opinion', label: '办理意见', align: 'center' }
 ]);
 // 定义变量内容
 const ruleFormRef = ref<RefType>(); // 表单ref

+ 147 - 71
src/views/statistics/center/detailTwist.vue

@@ -11,13 +11,13 @@
 				v-model:page-index="state.queryParams.PageIndex"
 				v-model:page-size="state.queryParams.PageSize"
 				:toolButton="['refresh', 'setting', 'exportCurrent', 'exportAll']"
-				:exportMethod="departmentSatisfactionListExport"
+				:exportMethod="visitTurnExport"
 				:exportParams="requestParams"
 			>
 				<template #table-search>
 					<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent label-width="100px" inline>
-								<el-form-item label="工单标题" prop="Keyword">
-									<el-input v-model="state.queryParams.Keyword" placeholder="工单标题" clearable @keyup.enter="handleQuery" class="keyword-input"/>
+								<el-form-item label="工单标题" prop="Title">
+									<el-input v-model="state.queryParams.Title" placeholder="工单标题" clearable @keyup.enter="handleQuery" class="keyword-input"/>
 								</el-form-item>
 								<el-form-item label="工单编码" prop="No">
 									<el-input v-model="state.queryParams.No" placeholder="工单编码" clearable @keyup.enter="handleQuery" class="keyword-input"/>
@@ -34,10 +34,10 @@
 					</el-button>
 				</template>
 				<template #title="{ row }">
-					<order-detail :order="row" @updateList="queryList">{{ row.title }}</order-detail>
+					<order-detail :order="row.order" @updateList="queryList">{{ row.order?.title }}</order-detail>
 				</template>
 				<template #expiredStatusText="{ row }">
-					<span :class="'overdue-status-' + row.expiredStatus" :title="row.expiredStatusText"></span>
+					<span :class="'overdue-status-' + row.order?.expiredStatus" :title="row.order?.expiredStatusText"></span>
 				</template>
 				<template #operation="{ row }">
 					<el-button link type="primary" @click="visitDetail(row)" title="查看回访详情"> 回访详情 </el-button>
@@ -47,45 +47,58 @@
 		<!--	更多查询	-->
 		<el-drawer v-model="drawer" title="更多查询" size="500px">
 			<el-form :model="state.queryParams" ref="drawerRuleFormRef" @submit.native.prevent label-width="100px">
-				<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"
+				<el-form-item label="热线号码" prop="TransferPhone">
+					<el-select
+						v-model="state.queryParams.TransferPhone"
+						placeholder="请选择热线号码"
+						clearable
+						class="w100"
 						@change="handleQuery"
-						value-format="YYYY-MM-DD[T]HH:mm:ss"
-						:default-time="defaultTimeStartEnd"
-						:clearable="false"
-					/>
+					>
+						<el-option v-for="item in state.callForwardingOptions" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
+					</el-select>
+				</el-form-item>
+				<el-form-item label="归档类型" prop="FiledType">
+					<el-select
+						v-model="state.queryParams.FiledType"
+						placeholder="请选择归档类型"
+						clearable
+						class="w100"
+						@change="handleQuery"
+					>
+						<el-option v-for="item in state.filedTypeOptions" :value="item.key" :key="item.key" :label="item.value" />
+					</el-select>
 				</el-form-item>
-				<el-form-item label="回访人姓名" prop="VisitUser">
-					<el-input v-model="state.queryParams.VisitUser" placeholder="回访人姓名" clearable @keyup.enter="handleQuery" />
+				<el-form-item label="回访人" prop="VisitUserName">
+					<el-input v-model="state.queryParams.VisitUserName" placeholder="回访人" clearable @keyup.enter="handleQuery" />
 				</el-form-item>
-				<el-form-item label="办件结果" prop="OrgProcessingResults">
+				<el-form-item label="来电号码" prop="FromPhone">
+					<el-input v-model="state.queryParams.FromPhone" placeholder="来电号码" clearable @keyup.enter="handleQuery" />
+				</el-form-item>
+				<el-form-item label="受理类型" prop="AcceptType">
 					<el-select
-						v-model="state.queryParams.OrgProcessingResults"
-						placeholder="请选择来办件结果"
+						v-model="state.queryParams.AcceptType"
+						placeholder="请选择受理类型"
 						clearable
 						class="w100"
 						@change="handleQuery"
 					>
-						<el-option v-for="item in state.visitSatisfaction" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
+						<el-option v-for="item in state.acceptTypeOptions" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
 					</el-select>
 				</el-form-item>
 				<el-form-item label="热点分类" prop="Hotspot">
 					<el-input v-model="state.queryParams.Hotspot" placeholder="热点分类名称" clearable @keyup.enter="handleQuery" />
 				</el-form-item>
-				<el-form-item label="回访部门" prop="OrgId">
+				<el-form-item label="受理人" prop="NameOrNo">
+					<el-input v-model="state.queryParams.NameOrNo" placeholder="受理人" clearable @keyup.enter="handleQuery" />
+				</el-form-item>
+				<el-form-item label="接办部门" prop="OrgId">
 					<el-cascader
 						:options="state.orgsOptions"
 						filterable
 						:show-all-levels="false"
 						:props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false }"
-						placeholder="请选择回访部门"
+						placeholder="请选择接办部门"
 						clearable
 						class="w100"
 						v-model="state.queryParams.OrgId"
@@ -93,6 +106,21 @@
 					>
 					</el-cascader>
 				</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="handleQuery"
+						value-format="YYYY-MM-DD[T]HH:mm:ss"
+						:default-time="defaultTimeStartEnd"
+						:clearable="false"
+					/>
+				</el-form-item>
 				<el-form-item label="办结时间" prop="doneTime">
 					<el-date-picker
 						v-model="state.queryParams.doneTime"
@@ -107,11 +135,29 @@
 						:default-time="defaultTimeStartEnd"
 					/>
 				</el-form-item>
-				<el-form-item label="来电主体" prop="TypeCode">
-					<el-select v-model="state.queryParams.TypeCode" placeholder="请选择来电主体" @change="handleQuery">
-						<el-option :value="0" label="全部" />
-						<el-option :value="1" label="市民" />
-						<el-option :value="2" label="企业" />
+				<el-form-item label="回访时间" prop="hfTime">
+					<el-date-picker
+						v-model="state.queryParams.hfTime"
+						type="datetimerange"
+						unlink-panels
+						range-separator="至"
+						start-placeholder="开始时间"
+						end-placeholder="结束时间"
+						:shortcuts="shortcuts"
+						@change="handleQuery"
+						value-format="YYYY-MM-DD[T]HH:mm:ss"
+						:default-time="defaultTimeStartEnd"
+					/>
+				</el-form-item>
+				<el-form-item label="评判状态" prop="JudgeState">
+					<el-select
+						v-model="state.queryParams.JudgeState"
+						placeholder="请选择评判状态"
+						clearable
+						class="w100"
+						@change="handleQuery"
+					>
+						<el-option v-for="item in state.visitSatisfaction" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
 					</el-select>
 				</el-form-item>
 			</el-form>
@@ -127,13 +173,13 @@
 <script setup lang="tsx" name="statisticsCenterDetailTwist">
 import { onMounted, reactive, ref, defineAsyncComponent } from 'vue';
 import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
-import { departmentSatisfactionDetailBase, departmentSatisfactionList, departmentSatisfactionListExport } from '@/api/statistics/department';
 import { formatDate } from '@/utils/formatTime';
-import { defaultDateOne, defaultTimeStartEnd, shortcuts } from '@/utils/constants';
+import { defaultTimeStartEnd, shortcuts } from '@/utils/constants';
 import { useRoute } from 'vue-router';
 import Other from '@/utils/other';
 import { exportJbOrder } from '@/api/business/order';
 import { downloadZip } from '@/utils/tools';
+import { visitTurnList,visitTurnBaseData,visitTurnExport } from '@/api/business/visit';
 
 // 引入组件
 const VisitDetailCom = defineAsyncComponent(() => import('@/views/business/visit/component/Visit-detail.vue')); // 回访
@@ -141,43 +187,43 @@ const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/
 // 表格配置项
 const columns = ref<any[]>([
 	{ type: 'selection', width: 40, align: 'center' },
-	{ prop: 'expiredStatusText', label: '超期状态', align: 'center', minWidth: 80 },
-	{ prop: 'statusText', label: '工单状态', width: 100 },
-	{ prop: 'sourceChannel', label: '来源渠道', minWidth: 100 },
-	{ prop: 'currentStepName', label: '当前节点', minWidth: 100 },
-	{ prop: 'no', label: '工单编码', minWidth: 140 },
+	{ prop: 'order.expiredStatusText', label: '超期状态', align: 'center', minWidth: 80 },
+	{ prop: 'order.statusText', label: '工单状态', width: 100 },
+	{ prop: 'order.sourceChannel', label: '来源渠道', minWidth: 100 },
+	{ prop: 'order.currentStepName', label: '当前节点', minWidth: 100 },
+	{ prop: 'order.no', label: '工单编码', minWidth: 140 },
 	{
-		prop: 'creationTime',
+		prop: 'order.startTime',
 		label: '受理时间',
 		minWidth: 160,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.creationTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
+		render: (scope:any) => {
+			return <span>{formatDate(scope.row.order.startTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
 		},
 	},
-	{ prop: 'title', label: '工单标题', width: 200 },
-	{ prop: 'fromPhone', label: '来电号码', minWidth: 130 },
-	{ prop: 'currentHandleOrgName', label: '接办部门', minWidth: 140 },
+	{ prop: 'order.title', label: '工单标题', width: 200 },
+	{ prop: 'order.fromPhone', label: '来电号码', minWidth: 130 },
+	{ prop: 'order.currentHandleOrgName', label: '接办部门', minWidth: 140 },
 	{
-		prop: 'actualHandleTime',
+		prop: 'order.actualHandleTime',
 		label: '接办时间',
 		minWidth: 160,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.actualHandleTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
+		render: (scope:any) => {
+			return <span>{formatDate(scope.row.order?.actualHandleTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
 		},
 	},
 	{
-		prop: 'filedTime',
+		prop: 'order.filedTime',
 		label: '办结时间',
 		minWidth: 160,
-		render: (scope) => {
-			return <span>{formatDate(scope.row.filedTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
+		render: (scope:any) => {
+			return <span>{formatDate(scope.row.order?.filedTime, 'YYYY-mm-dd HH:MM:SS')}</span>;
 		},
 	},
-	{ prop: 'visitUser', label: '回访人', minWidth: 120 },
-	{ prop: 'visitUser', label: '评判状态', minWidth: 100 },
-	{ prop: 'acceptType', label: '受理类型', minWidth: 100 },
-	{ prop: 'hotspotName', label: '热点分类', minWidth: 150 },
-	{ prop: 'acceptorName', label: '受理人', minWidth: 120 },
+	{ prop: 'employeeName', label: '回访人', minWidth: 120 },
+	{ prop: 'judgeStateText', label: '评判状态', minWidth: 100 },
+	{ prop: 'order.acceptType', label: '受理类型', minWidth: 100 },
+	{ prop: 'order.hotspotName', label: '热点分类', minWidth: 150 },
+	{ prop: 'order.acceptorName', label: '受理人', minWidth: 120 },
 	{ prop: 'operation', label: '操作', fixed: 'right', width: 90, align: 'center' },
 ]);
 // 定义变量内容
@@ -187,22 +233,36 @@ const state = reactive<any>({
 		// 查询条件
 		PageIndex: 1,
 		PageSize: 20,
-		HotspotIds: null,
-		crTime: defaultDateOne,
+		Title: null,
+		No:null,
+		VisitUserName:null,
+		FromPhone:null,
+		AcceptType:null,
+		Hotspot:null,
+		OrgId:null,
+		NameOrNo:null,
+		crTime: [], // 受理时间
 		CreationTimeStart: null,
 		CreationTimeEnd: null,
 		doneTime: [], // 办结时间
 		ActualHandleTimeStart: null,
 		ActualHandleTimeEnd: null,
-		VisitUser: null,
-		OrgProcessingResults: null,
-		OrgId:null,
-		TypeId:'1',
-		TypeCode:0
+		hfTime:[], // 回访时间
+		VisitTimeStart:null,
+		VisitTimeEnd:null,
+		JudgeState:null,
+		TransferPhone:null,
+		FiledType:null,
+		OrgJudge:null, // 部门扭转
+		SeatJudge:null, // 坐席扭转
 	},
 	tableData: [], //表单
 	loading: false, // 加载
 	total: 0, // 总数
+	acceptTypeOptions:[],
+	callForwardingOptions:[],
+	filedTypeOptions:[],
+	orgsOptions:[],
 });
 /** 搜索按钮操作 */
 const handleQuery = () => {
@@ -213,17 +273,30 @@ const handleQuery = () => {
 /** 获取列表 */
 const route = useRoute();
 const routeQueryParams = route.query;
-const requestParams = ref({});
+const requestParams = ref<EmptyObjectType>({});
 const queryList = () => {
 	state.loading = true;
+	if(routeQueryParams.type){
+		switch (routeQueryParams.type) {
+				case 'department':
+					state.queryParams.OrgJudge = true;
+					break;
+				case 'seat':
+					state.queryParams.SeatJudge = true;
+					break;
+		}
+	}
 	requestParams.value = Other.deepClone(state.queryParams);
 	requestParams.value.CreationTimeStart = state.queryParams.crTime === null ? null : state.queryParams.crTime[0]; // 生成时间
 	requestParams.value.CreationTimeEnd = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
-	Reflect.deleteProperty(requestParams.value, 'crTime'); // 删除无用的参数
-	requestParams.value.ActualHandleTimeStart = state.queryParams.doneTime === null ? null : state.queryParams.doneTime[0]; // 生成时间
+	Reflect.deleteProperty(requestParams.value, 'crTime'); // 受理时间
+	requestParams.value.ActualHandleTimeStart = state.queryParams.doneTime === null ? null : state.queryParams.doneTime[0];
 	requestParams.value.ActualHandleTimeEnd = state.queryParams.doneTime === null ? null : state.queryParams.doneTime[1];
-	Reflect.deleteProperty(requestParams.value, 'doneTime'); // 删除无用的参数
-	departmentSatisfactionList(requestParams.value)
+	Reflect.deleteProperty(requestParams.value, 'doneTime'); // 办结时间
+	requestParams.value.VisitTimeStart = state.queryParams.hfTime === null ? null : state.queryParams.hfTime[0];
+	requestParams.value.VisitTimeEnd = state.queryParams.hfTime === null ? null : state.queryParams.hfTime[1];
+	Reflect.deleteProperty(requestParams.value, 'hfTime'); // 回访时间
+	visitTurnList(requestParams.value)
 		.then((res: any) => {
 			state.tableData = res.result?.items ?? [];
 			state.total = res.result.total ?? 0;
@@ -245,15 +318,17 @@ const resetQuery = (formEl: FormInstance | undefined) => {
 // 回访详情
 const visitDetailRef = ref<RefType>();
 const visitDetail = (row: any) => {
-	visitDetailRef.value.openDialog({ ...row, id: row.visitId }, '回访详情');
+	visitDetailRef.value.openDialog(row, '回访详情');
 };
 // 获取查询条件基础信息
 const getBaseData = async () => {
 	try {
-		const res: any = await departmentSatisfactionDetailBase();
+		const res: any = await visitTurnBaseData();
 		const mappings: any = {
-			visitSatisfaction: 'visitSatisfaction',
+			acceptTypeOptions: 'acceptTypeOptions',
+			callForwardingOptions:'callForwardingOptions',
 			orgsOptions: 'orgsOptions',
+			filedTypeOptions:'filedTypeOptions'
 		};
 		for (const key in mappings) {
 			state[key] = res.result?.[mappings[key]] ?? [];
@@ -265,7 +340,7 @@ const getBaseData = async () => {
 // 交办单导出
 const proTableRef = ref<RefType>();
 const onJbExport = () => {
-	const ids = proTableRef.value.selectedList.map((item: any) => item.id);
+	const ids = proTableRef.value.selectedList.map((item: any) => item.order?.id);
 	ElMessageBox.confirm(`您确定导出选中的${proTableRef.value.selectedList.length}个工单的交办单,是否继续?`, '提示', {
 		confirmButtonText: '确认',
 		cancelButtonText: '取消',
@@ -289,6 +364,7 @@ const onJbExport = () => {
 		.catch(() => {});
 };
 onMounted(() => {
+	ruleFormRef.value?.resetFields();
 	getBaseData();
 	queryList();
 });

+ 12 - 14
src/views/statistics/center/twist.vue

@@ -9,9 +9,8 @@
 				:loading="state.loading"
 				:pagination="false"
 				:toolButton="['refresh', 'setting', 'exportAll']"
-				:exportMethod="centerReturnVisitSourceExport"
+				:exportMethod="centerTurnExport"
 				:exportParams="requestParams"
-				show-summary
 			>
 				<template #table-search>
 					<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
@@ -72,22 +71,22 @@
 <script setup lang="tsx" name="statisticsCenterTwist">
 import { onMounted, reactive, ref } from 'vue';
 import { FormInstance } from 'element-plus';
-import { centerReturnVisitSourceExport, centerReturnVisitSource } from '@/api/statistics/center';
+import { centerTurn,centerTurnExport } from '@/api/statistics/center';
 import { defaultDate, defaultTimeStartEnd, shortcuts } from '@/utils/constants';
 import Other from '@/utils/other';
 import { useRouter } from 'vue-router';
 
 // 表格配置项
 const columns = ref<any[]>([
-	{ prop: 'sourceChannel', label: '回访员' },
-	{ prop: 'count', label: '部门扭转总件' },
-	{ prop: 'count', label: '部门待审批件' },
-	{ prop: 'count', label: '部门审批成功件' },
-	{ prop: 'count', label: '部门审批失败件' },
-	{ prop: 'count', label: '坐席扭转总件' },
-	{ prop: 'count', label: '坐席待审批件' },
-	{ prop: 'count', label: '坐席审批成功件' },
-	{ prop: 'count', label: '坐席审批失败件' },
+	{ prop: 'empName', label: '回访员' },
+	{ prop: 'orgJudeCount', label: '部门扭转总件' },
+	{ prop: 'orgJudeApprovalingCount', label: '部门待审批件' },
+	{ prop: 'orgJudeSuccessCount', label: '部门审批成功件' },
+	{ prop: 'orgJudeFailCount', label: '部门审批失败件' },
+	{ prop: 'seatJudeCount', label: '坐席扭转总件' },
+	{ prop: 'seatJudeApprovalingCount', label: '坐席待审批件' },
+	{ prop: 'seatJudeSuccessCount', label: '坐席审批成功件' },
+	{ prop: 'seatJudeFailCount', label: '坐席审批失败件' },
 ]);
 // 定义变量内容
 const ruleFormRef = ref<RefType>(); // 表单ref
@@ -114,7 +113,7 @@ const queryList = () => {
 	requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
 	requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
 	Reflect.deleteProperty(requestParams.value, 'crTime');
-	centerReturnVisitSource(requestParams.value)
+	centerTurn(requestParams.value)
 		.then((res: any) => {
 			state.tableData = res.result ?? [];
 			state.loading = false;
@@ -138,7 +137,6 @@ const onDetail = (type: string) => {
 				path: '/statistics/center/detailTwist',
 				query: {
 					tagsViewName: '扭转回访明细',
-					type,
 				},
 			});
 			break;

+ 39 - 9
src/views/tels/callLog/component/Play-record.vue

@@ -1,6 +1,16 @@
 <template>
-	<div class="tels-callRecord-playRecord-container">
-		<el-dialog v-model="dialogVisible" width="500px" draggable title="播放录音" append-to-body destroy-on-close>
+		<el-dialog
+			v-model="dialogVisible"
+			width="500px"
+			draggable
+			title="播放录音"
+			append-to-body
+			destroy-on-close
+			:modal="false"
+			modal-class="modal_class"
+			class="dialog_class"
+			:close-on-click-modal="false"
+		>
 			<!--			<el-row :gutter="10" class="mb10">
 				<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12"
 					><div class="item">
@@ -13,9 +23,14 @@
 					</div>
 				</el-col>
 			</el-row>-->
-			<audio-player :url="RecordUrl" :fileName="fileName" :recordingAbsolutePath="recordingAbsolutePath" />
+			<el-table>
+				<el-table-column prop="name" label="录音名称" width="180" />
+				<el-table-column prop="createTime" label="创建时间" width="180" />
+				<el-table-column prop="duration" label="时长" width="180" />
+				<el-table-column prop="size" label="大小" width="180" />
+			</el-table>
+			<audio-player :url="RecordUrl" :fileName="fileName" :recordingAbsolutePath="recordingAbsolutePath" :key="RecordUrl"/>
 		</el-dialog>
-	</div>
 </template>
 
 <script lang="ts" setup name="callRecordPlayRecord">
@@ -32,16 +47,16 @@ const fromPhone = ref<string>(''); // 来电号码
 const createTime = ref<string>(''); // 创建时间
 const dialogVisible = ref<boolean>(false); //是否显示弹窗
 // 打开弹窗
-const openDialog = async (url: any, recordingFileName, recordingPath, phone, time) => {
+const openDialog = async (url: string, recordingFileName, recordingPath, phone, time) => {
 	RecordUrl.value = url;
 	fileName.value = recordingFileName;
 	recordingAbsolutePath.value = recordingPath;
 	fromPhone.value = phone;
 	createTime.value = time;
-	/*	dialogVisible.value = true;
-	emit('openDialog');*/
+	dialogVisible.value = true;
+	emit('openDialog');
 
-	let popup = window.open('', '_blank', 'left=100,top=100,width=400,height=200');
+	/*let popup = window.open('', '_blank', 'left=100,top=100,width=400,height=200');
 	if (popup) {
 		popup.document.write(`
         <html>
@@ -92,7 +107,7 @@ const openDialog = async (url: any, recordingFileName, recordingPath, phone, tim
       `);
 	} else {
 		alert('浏览器阻止了弹出窗口,请允许弹出窗口后再试。');
-	}
+	}*/
 };
 // 关闭弹窗
 const closeDialog = () => {
@@ -117,3 +132,18 @@ defineExpose({ closeDialog, openDialog });
 	}
 }
 </style>
+<style lang="scss">
+.modal_class {
+	pointer-events: none;
+	height: 100%;
+}
+.dialog_class {
+	pointer-events: auto;
+	flex-direction: column;
+	margin: 0 !important;
+	position: absolute;
+	top: 20%;
+	left: 25%;
+}
+</style>
+

+ 2 - 0
src/views/tels/callLog/index.vue

@@ -10,6 +10,7 @@ import { storeToRefs } from 'pinia';
 // 引入组件
 const YiBin = defineAsyncComponent(() => import('@/views/tels/callLog/ybCallLog.vue')); // 宜宾通话记录
 const ZiGong = defineAsyncComponent(() => import('@/views/tels/callLog/zgCallLog.vue')); // 自贡通话记录
+const LuZhou = defineAsyncComponent(() => import('@/views/tels/callLog/zgCallLog.vue')); // 泸州通话记录
 
 const storesThemeConfig = useThemeConfig();
 const { themeConfig } = storeToRefs(storesThemeConfig);
@@ -17,6 +18,7 @@ const { themeConfig } = storeToRefs(storesThemeConfig);
 const COMPONENT_LIST = {
 	YiBin,
 	ZiGong,
+	LuZhou
 };
 // 当前地州市
 const currentCity = computed(() => {

+ 1 - 1
src/views/tels/callLog/ybCallLog.vue

@@ -508,7 +508,7 @@ import { useThemeConfig } from '@/stores/themeConfig';
 import { storeToRefs } from 'pinia';
 
 // 引入组件
-const PlayRecord = defineAsyncComponent(() => import('@/views/tels/callLog/component/Play-record.vue')); // 播放录音
+const PlayRecord = defineAsyncComponent(() => import('@/components/PlayRecord/index.vue')); // 播放录音
 const ConnectBusiness = defineAsyncComponent(() => import('@/views/tels/callLog/component/Connect-business.vue')); // 关联工单还是回访
 const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
 

+ 1 - 1
src/views/tels/callLog/zgCallLog.vue

@@ -252,7 +252,7 @@ import { useThemeConfig } from '@/stores/themeConfig';
 import { storeToRefs } from 'pinia';
 
 // 引入组件
-const PlayRecord = defineAsyncComponent(() => import('@/views/tels/callLog/component/Play-record.vue')); // 播放录音
+const PlayRecord = defineAsyncComponent(() => import('@/components/PlayRecord/index.vue')); // 播放录音
 const ConnectBusiness = defineAsyncComponent(() => import('@/views/tels/callLog/component/Connect-business.vue')); // 关联工单还是回访
 const OrderDetail = defineAsyncComponent(() => import('@/components/OrderDetail/index.vue')); // 工单详情
 

+ 2 - 0
src/views/todo/seats/accept/index.vue

@@ -10,6 +10,7 @@ import { storeToRefs } from 'pinia';
 // 引入组件
 const YiBin = defineAsyncComponent(() => import('@/views/todo/seats/accept/ybAccept.vue')); // 宜宾工单受理
 const ZiGong = defineAsyncComponent(() => import('@/views/todo/seats/accept/zgAccept.vue')); // 自贡工单受理
+const LuZhou = defineAsyncComponent(() => import('@/views/todo/seats/accept/zgAccept.vue')); // 泸州工单受理
 
 const storesThemeConfig = useThemeConfig();
 const { themeConfig } = storeToRefs(storesThemeConfig);
@@ -17,6 +18,7 @@ const { themeConfig } = storeToRefs(storesThemeConfig);
 const COMPONENT_LIST = {
 	YiBin,
 	ZiGong,
+	LuZhou
 };
 // 当前地州市
 const currentCity = computed(() => {